From d7e61abf2b56246ef5a8311acfc20be21ba6639a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Thu, 16 Jul 2026 18:31:28 +0400 Subject: [PATCH] chore: commit all changes --- TEST_STAND/POSTGRES/main.tf | 8 +- TEST_STAND/POSTGRES/nubes_postgres.tf | 70 +++++---- TEST_STAND/POSTGRES/userUNDdb.tf | 47 +++--- TOOLS/config/dev/profile.env | 4 +- TOOLS/config/prod/profile.env | 4 +- TOOLS/config/test/profile.env | 6 +- .../internal/helpers/helpers.go | 19 ++- TOOLS/scripts/05_generate_docs_llm.py | 134 ++++++++++++++++++ secrets/llm.key | 13 ++ 9 files changed, 234 insertions(+), 71 deletions(-) create mode 100644 TOOLS/scripts/05_generate_docs_llm.py create mode 100644 secrets/llm.key diff --git a/TEST_STAND/POSTGRES/main.tf b/TEST_STAND/POSTGRES/main.tf index 399531d..cf1f7a5 100644 --- a/TEST_STAND/POSTGRES/main.tf +++ b/TEST_STAND/POSTGRES/main.tf @@ -1,8 +1,8 @@ terraform { required_providers { nubes = { - source = "terra.k8c.ru/nubes-test/nubes" - version = "5.0.64" + source = "registry.kube5s.ru/nubes-test/nubes" + version = "5.1.2" } } } @@ -22,6 +22,10 @@ variable "realm" { sensitive = true description = "resource_realm parameter for nubes_postgres resource" } +variable "s3_user_uid" { + type = string + description = "S3 user UUID" +} provider "nubes" { api_token = var.api_token diff --git a/TEST_STAND/POSTGRES/nubes_postgres.tf b/TEST_STAND/POSTGRES/nubes_postgres.tf index add5e23..4e1420e 100644 --- a/TEST_STAND/POSTGRES/nubes_postgres.tf +++ b/TEST_STAND/POSTGRES/nubes_postgres.tf @@ -1,50 +1,48 @@ resource "nubes_postgres" "npg" { resource_name = "pgtst01" - startup_configuration = jsonencode({ - resourceRealm = var.realm - }) + startup_configuration = { + resource_realm = var.realm + } - cluster_configuration = jsonencode({ - cpu = "500" - memory = "512" - replicas = "1" - disk = "10" - }) + cluster_configuration = { + cpu = 500 + memory = 512 + replicas = 1 + disk = 10 + } - access_configuration = jsonencode({ - masterIpSpace = "no-needed" - masterAccessList = ["10.0.0.0/8"] - slaveIpSpace = "no-needed" - slaveAccessList = [] - }) + access_configuration = { + master_ip_space = "no-needed" + master_access_list = jsonencode(["10.0.0.0/8"]) + slave_ip_space = "no-needed" + slave_access_list = jsonencode([]) + } - postgres_configuration = jsonencode({ - version = "17" - sslRequired = true - poolerMaster = false - poolerSlave = false - }) + postgres_configuration = { + version = "17" + ssl_required = true + pooler_master = false + pooler_slave = false + } - postgres_conf = jsonencode([ - { - paramName = "log_connections" - paramValue = "''" - } - ]) + postgres_conf = jsonencode([{ + param_name = "log_connections" + param_value = "" + }]) - backup_configuration = jsonencode({ - s3Uid = var.s3_uid - retain = "14" + backup_configuration = { + s3_uid = var.s3_uid + retain = 14 schedule = "0 0 * * *" - }) + } - autoscale_configuration = jsonencode({ + autoscale_configuration = { enabled = false - schedule = "0" - percent = "10" - quota = "100" - }) + schedule = 0 + percent = 10 + quota = 100 + } operation_timeout = "11m" adopt_existing_on_create = true diff --git a/TEST_STAND/POSTGRES/userUNDdb.tf b/TEST_STAND/POSTGRES/userUNDdb.tf index 86c7edc..7145493 100644 --- a/TEST_STAND/POSTGRES/userUNDdb.tf +++ b/TEST_STAND/POSTGRES/userUNDdb.tf @@ -8,22 +8,22 @@ resource "nubes_postgres_user" "pg_user_0" { adopt_existing_on_create = true } -resource "nubes_postgres_user" "pg_user_1" { - postgres_id = nubes_postgres.npg.id - username = "user1" - role = "ddl_user" - adopt_existing_on_create = true -} +# resource "nubes_postgres_user" "pg_user_1" { +# postgres_id = nubes_postgres.npg.id +# username = "user1" +# role = "ddl_user" +# adopt_existing_on_create = true +# } -# ============================================================================= -# PostgreSQL — базы данных (3 шт.) -# ============================================================================= -resource "nubes_postgres_database" "pg_db_1" { - postgres_id = nubes_postgres.npg.id - db_name = "dbapp1" - db_owner = nubes_postgres_user.pg_user_1.username - adopt_existing_on_create = true -} +# # ============================================================================= +# # PostgreSQL — базы данных (3 шт.) +# # ============================================================================= +# resource "nubes_postgres_database" "pg_db_1" { +# postgres_id = nubes_postgres.npg.id +# db_name = "dbapp1" +# db_owner = nubes_postgres_user.pg_user_1.username +# adopt_existing_on_create = true +# } resource "nubes_postgres_database" "pg_db_2" { postgres_id = nubes_postgres.npg.id @@ -32,9 +32,18 @@ resource "nubes_postgres_database" "pg_db_2" { adopt_existing_on_create = true } -resource "nubes_postgres_database" "pg_db_3" { - postgres_id = nubes_postgres.npg.id - db_name = "dbapp3" - db_owner = nubes_postgres_user.pg_user_1.username +# resource "nubes_postgres_database" "pg_db_3" { +# postgres_id = nubes_postgres.npg.id +# db_name = "dbapp3" +# db_owner = nubes_postgres_user.pg_user_1.username +# adopt_existing_on_create = true +# } + +# S3 bucket — замени "buck0" на своё имя везде ниже +resource "nubes_s3bucket" "bukka0" { # ← замени buck0 на своё имя ресурса + resource_name = "btst" # ← замени buck0 на своё имя ресурса + #s3_user_uid = "naeel-s3" + s3_user_uid = var.s3_user_uid + bucket_name = "buck01" # ← замени buck0 на своё имя бакета adopt_existing_on_create = true } diff --git a/TOOLS/config/dev/profile.env b/TOOLS/config/dev/profile.env index 3d53b85..decb299 100644 --- a/TOOLS/config/dev/profile.env +++ b/TOOLS/config/dev/profile.env @@ -7,8 +7,8 @@ TOKEN_FILE="secrets/dev.token" VERSION="3.1.0" # Registry/S3 settings -REGISTRY_HOST="terra.k8c.ru" -REGISTRY_HOSTNAME="terra.k8c.ru" +REGISTRY_HOST="registry.kube5s.ru" +REGISTRY_HOSTNAME="registry.kube5s.ru" NAMESPACE="nubes-dev" PROVIDER_NAME="nubes" S3_BUCKET="terraform-registry" diff --git a/TOOLS/config/prod/profile.env b/TOOLS/config/prod/profile.env index 3543b90..e064b83 100644 --- a/TOOLS/config/prod/profile.env +++ b/TOOLS/config/prod/profile.env @@ -7,8 +7,8 @@ TOKEN_FILE="secrets/prod.token" VERSION="2.1.0" # Registry/S3 settings -REGISTRY_HOST="terra.k8c.ru" -REGISTRY_HOSTNAME="terra.k8c.ru" +REGISTRY_HOST="registry.kube5s.ru" +REGISTRY_HOSTNAME="registry.kube5s.ru" NAMESPACE="nubes-prod" PROVIDER_NAME="nubes" S3_BUCKET="terraform-registry" diff --git a/TOOLS/config/test/profile.env b/TOOLS/config/test/profile.env index aa85d19..008f4c6 100644 --- a/TOOLS/config/test/profile.env +++ b/TOOLS/config/test/profile.env @@ -3,14 +3,14 @@ NUBES_API_ENDPOINT="https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" TOKEN_FILE="secrets/test.token" # Version -VERSION="5.1.0" +VERSION="5.1.2" # Docs generation — ONLY from docs_gen// (never from docs/) DOCS_GEN_DIR="provider/docs_gen/test" # Registry/S3 settings -REGISTRY_HOST="terra.k8c.ru" -REGISTRY_HOSTNAME="terra.k8c.ru" +REGISTRY_HOST="registry.kube5s.ru" +REGISTRY_HOSTNAME="registry.kube5s.ru" NAMESPACE="nubes-test" PROVIDER_NAME="nubes" S3_BUCKET="terraform-registry" diff --git a/TOOLS/resource-generator/internal/helpers/helpers.go b/TOOLS/resource-generator/internal/helpers/helpers.go index e3abf12..de7b58b 100644 --- a/TOOLS/resource-generator/internal/helpers/helpers.go +++ b/TOOLS/resource-generator/internal/helpers/helpers.go @@ -305,13 +305,18 @@ func NestedJSONExpr(p types.Param, varPrefix string) string { for _, sp := range p.SubParams { field := varName + "." + ToCamel(sp.Code) var valExpr string - switch strings.ToLower(sp.Type) { - case "bool": - valExpr = fmt.Sprintf(`fmt.Sprintf("%%v", %s.ValueBool())`, field) - case "int64", "int", "number": - valExpr = fmt.Sprintf(`fmt.Sprintf("%%d", %s.ValueInt64())`, field) - default: - valExpr = fmt.Sprintf(`fmt.Sprintf("\"%%s\"", %s.ValueString())`, field) + // IsJson проверяем до Type — для json полей Type="string", но сериализация другая. + if sp.IsJson { + valExpr = fmt.Sprintf(`%s.ValueString()`, field) + } else { + switch strings.ToLower(sp.Type) { + case "bool": + valExpr = fmt.Sprintf(`fmt.Sprintf("%%v", %s.ValueBool())`, field) + case "int64", "int", "number": + valExpr = fmt.Sprintf(`fmt.Sprintf("%%d", %s.ValueInt64())`, field) + default: + valExpr = fmt.Sprintf(`fmt.Sprintf("\"%%s\"", %s.ValueString())`, field) + } } pairs = append(pairs, fmt.Sprintf(`"%s": %s`, sp.Code, valExpr)) } diff --git a/TOOLS/scripts/05_generate_docs_llm.py b/TOOLS/scripts/05_generate_docs_llm.py new file mode 100644 index 0000000..baee6e1 --- /dev/null +++ b/TOOLS/scripts/05_generate_docs_llm.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""LLM-генератор документации для Nubes Terraform Provider. +Читает YAML-спеки, отправляет в LLM, сохраняет Markdown. +""" +import json, os, sys, time, yaml +from pathlib import Path +from urllib.request import Request, urlopen +from urllib.error import URLError + +API_URL = "https://api.aillm.ru/v1/chat/completions" +API_KEY = "sk-ucI5YvOticoOQ9Kuj5K9mQ" +MODEL = "gpt-oss-120b" + +PROMPT = """Ты — генератор документации Terraform-провайдера Nubes Cloud. +Отвечай ТОЛЬКО Markdown-кодом. Никаких пояснений до или после. + +⛔ ЖЁСТКИЕ ПРАВИЛА (нарушать нельзя): + +1. НЕ ВЫДУМЫВАЙ параметры. Только те, что есть в YAML. + Если у параметра нет description — напиши "—". + НИКОГДА не придумывай password, role, encoding и т.п. + +2. Action-операции. ТОЛЬКО redeploy включается в документацию. + restart, recovery, reconcile — ИСКЛЮЧИТЬ. + +3. Subresource-операции. Каждый subresource → отдельная секция. + Имя ресурса: nubes_{service}_{subresource}. + +4. Lifecycle. suspend_on_destroy=true → "terraform destroy = Suspend". + adopt_existing_on_create → "terraform apply может подхватить существующий". + +5. Outputs. Все поля из outputs.params. vault_secrets помечать как 🔒. + +6. MAN. Если service_man есть — вставить как есть в секцию ## MAN. + +7. ВЛОЖЕННЫЕ ПАРАМЕТРЫ (map-fixed/array-map-fixed). + Для каждого map-fixed параметра — показывать ВСЕ его sub_params как вложенную таблицу. + Для array-map-fixed — показывать структуру элемента. + Пример: + ### clusterConfiguration (map-fixed) + | Параметр | Тип | Обязательный | По умолчанию | Описание | + |---|---|---|---|---| + | cpu | integer > 0 | да | 500 | Количество CPU в милликорах | + | memory | integer > 0 | да | 512 | Память в MB | + +ФОРМАТ: +# Resource nubes_{name} +## MAN (если есть) +## Instance Operations (таблица) +## Create Parameters (таблица — для каждого map-fixed показывать sub_params) +## Modify Parameters (таблица) +## Subresources (таблица по каждому) +## Lifecycle +## Outputs (таблица) + +YAML-спек: +```yaml +{yaml_content} +```""" + +def call_llm(prompt_text: str) -> str: + data = json.dumps({ + "model": MODEL, + "messages": [{"role": "user", "content": prompt_text}], + "temperature": 0.1, + "max_tokens": 8192, + }).encode() + req = Request(API_URL, data=data, headers={ + "Authorization": f"Bearer {API_KEY}", + "Content-Type": "application/json", + }) + for attempt in range(3): + try: + with urlopen(req, timeout=120) as resp: + result = json.loads(resp.read()) + return result["choices"][0]["message"]["content"] + except Exception as e: + print(f" retry {attempt+1}/3: {e}", file=sys.stderr) + time.sleep(5) + raise RuntimeError("LLM failed after 3 retries") + +def strip_service_man(yaml_text: str) -> str: + """Убирает service_man (HTML-руководство) из YAML для сокращения токенов.""" + lines = yaml_text.split('\n') + out = [] + skip = False + for line in lines: + if line.strip().startswith('service_man:'): + out.append('service_man: "" # omitted for LLM token limit') + skip = True + continue + if skip: + # service_man может быть многострочным (HTML с отступами) + if line and line[0] not in (' ', '\t') and ':' in line: + skip = False + out.append(line) + continue + out.append(line) + return '\n'.join(out) + +def main(): + yaml_dir = sys.argv[1] if len(sys.argv) > 1 else "generated/test/resources_yaml" + docs_dir = sys.argv[2] if len(sys.argv) > 2 else "generated/test/docs_llm" + + os.makedirs(docs_dir, exist_ok=True) + yamls = sorted(Path(yaml_dir).glob("*.yaml")) + print(f"Processing {len(yamls)} services...") + + failed = [] + for i, yf in enumerate(yamls): + svc_name = yf.stem.split("_", 1)[1] if "_" in yf.stem else yf.stem + print(f" [{i+1}/{len(yamls)}] {svc_name}...", end=" ", flush=True) + + yaml_text = strip_service_man(yf.read_text()) + prompt = PROMPT.replace("{yaml_content}", yaml_text) + + try: + md = call_llm(prompt) + out = Path(docs_dir) / f"{yf.stem}.md" + out.write_text(md) + print("OK") + except Exception as e: + print(f"FAILED: {e}") + failed.append(svc_name) + + time.sleep(2) # rate limit + + if failed: + print(f"\nFailed ({len(failed)}): {', '.join(failed)}") + else: + print(f"\nAll {len(yamls)} OK") + +if __name__ == "__main__": + main() diff --git a/secrets/llm.key b/secrets/llm.key new file mode 100644 index 0000000..5031bf4 --- /dev/null +++ b/secrets/llm.key @@ -0,0 +1,13 @@ +Ntazetdinov@nubes.ru +https://api.aillm.ru/ +sk-ucI5YvOticoOQ9Kuj5K9mQ + + +Deepseek flash +sk-78ec529c1eba4ba69995091046c9fa33 + +cicd +eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoLWFwaSIsInN1YiI6IjAxOTllMzI1LTFjZGYtN2NkYS05MzE5LWU1MzAyYTg1ZTI5MSIsImV4cCI6MTc5ODI3MTUyMSwiaWF0IjoxNzgyNzE5NTIxLCJqdGkiOiI5NjQ2MDlmYy05ZGZiLTQ1YjMtYjk0NS1lNmE0NmUzMTA0MzQiLCJhdXRoX3RpbWUiOjAsInR5cCI6IiIsImF6cCI6IiIsInNlc3Npb25fc3RhdGUiOiIiLCJhY3IiOiIiLCJhbGxvd2VkLW9yaWdpbnMiOm51bGwsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6bnVsbH0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpudWxsfX0sInNjb3BlIjoiIiwic2lkIjoiIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiIiwiQ2xpZW50SUQiOiJXWjAxMzI1IiwiY29tcGFueV9pZCI6IjNlNjRhYWM2LWRjZmMtNDA4Mi04OGRjLWRhMTljODY1NTVhNSIsImNvbXBhbnlfbmFtZSI6ItCi0LXRgdGCIiwidG9rZW5fdHlwZSI6InRlY2giLCJpZHBfdXNyX3VpZCI6IjAxOTllMzI1LTFjZGYtN2NkYS05MzE5LWU1MzAyYTg1ZTI5MSIsImxvZ2luIjoidGF6ZXRAbmFyb2QucnUiLCJmaXJzdG5hbWUiOiLQndCw0LjQu9GMIiwibWlkZGxlbmFtZSI6ItCk0LDRgNC40YHQvtCy0LjRhyDQotC10YHRgtC-0LLQsNGPINGD0YfQtdGC0LrQsCIsImxhc3RuYW1lIjoi0KLQsNC30LXRgtC00LjQvdC-0LIiLCJncm91cHMiOm51bGwsInByZWZlcnJlZF91c2VybmFtZSI6IiIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImVtYWlsIjoidGF6ZXRAbmFyb2QucnUifQ.T2cSkKGlorUTr_ICpInwrZZ2Sqk_D-RHpibrj1VI-7Bg7CPvIKJ7n1QF9bJc9uqWH9cwQczrNsA8sROU3lnqUaa88hl_rMfP7UM_u8X_iG-_pKYD8tsckmmcos6keh2I9muSZ9Viy9LvLCZv3fY6nzMp2YT-KCQh-EDGZPgHSAToWs1uqiaKi99K-OcqckvaFNUsYbpLPVfnD_6UnDDKUmPjP4Ib24R4Z5qlmwUAxmgC6BfUcuqgk-2Mdj37ulWvdlBLd9ZoJv4jAvRffzclv2w-Qa8p3BEooC8wlZjTC3PU-ULR-Cd_N61Y31lkd953kkKE3_yGIQCbwCPYus1TiA + +gitea +1999993c70d97cc588cca420e59ac45d5290b734 \ No newline at end of file