diff --git a/.gitignore b/.gitignore index 434329c..95700b6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,11 @@ *.tfvars .terraform.lock.hcl -# === Generated docs (by stand — NEVER commit, regenerate from API) === -docs_gen/ -universal_rebuild/docs_gen/ +# === Generated files (NOT code — regenerate from API) === +provider/resources_yaml/ +provider/internal/resources_gen/ +generated/ +TOOLS/bin/ universal_rebuild/docs-gen/ # === Build artifacts (generated by devops scripts) === diff --git a/devops/ARCHITECTURE.md b/TOOLS/ARCHITECTURE.md similarity index 100% rename from devops/ARCHITECTURE.md rename to TOOLS/ARCHITECTURE.md diff --git a/devops/config/operation_timeouts.json b/TOOLS/config/dev/operation_timeouts.json similarity index 100% rename from devops/config/operation_timeouts.json rename to TOOLS/config/dev/operation_timeouts.json diff --git a/devops/profiles/dev/profile.env b/TOOLS/config/dev/profile.env similarity index 100% rename from devops/profiles/dev/profile.env rename to TOOLS/config/dev/profile.env diff --git a/devops/profiles/dev/services_list.txt b/TOOLS/config/dev/services_list.txt similarity index 100% rename from devops/profiles/dev/services_list.txt rename to TOOLS/config/dev/services_list.txt diff --git a/devops/profiles/dev/operation_timeouts.json b/TOOLS/config/prod/operation_timeouts.json similarity index 100% rename from devops/profiles/dev/operation_timeouts.json rename to TOOLS/config/prod/operation_timeouts.json diff --git a/devops/profiles/prod/profile.env b/TOOLS/config/prod/profile.env similarity index 100% rename from devops/profiles/prod/profile.env rename to TOOLS/config/prod/profile.env diff --git a/devops/config/services_list.txt b/TOOLS/config/prod/services_list.txt similarity index 100% rename from devops/config/services_list.txt rename to TOOLS/config/prod/services_list.txt diff --git a/devops/profiles/prod/operation_timeouts.json b/TOOLS/config/test/operation_timeouts.json similarity index 100% rename from devops/profiles/prod/operation_timeouts.json rename to TOOLS/config/test/operation_timeouts.json diff --git a/devops/profiles/test/profile.env b/TOOLS/config/test/profile.env similarity index 81% rename from devops/profiles/test/profile.env rename to TOOLS/config/test/profile.env index 832653e..2969326 100644 --- a/devops/profiles/test/profile.env +++ b/TOOLS/config/test/profile.env @@ -3,12 +3,12 @@ NUBES_API_ENDPOINT="https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" TOKEN_FILE="secrets/test.token" # Release versions -RELEASE_VERSION="5.0.59" -PROVIDER_VERSION="5.0.59" -DOCS_VERSION="5.0.59" +RELEASE_VERSION="5.0.60" +PROVIDER_VERSION="5.0.60" +DOCS_VERSION="5.0.60" # Docs generation — ONLY from docs_gen// (never from docs/) -DOCS_GEN_DIR="universal_rebuild/docs_gen/test" +DOCS_GEN_DIR="provider/docs_gen/test" # Registry/S3 settings REGISTRY_HOST="terra.k8c.ru" diff --git a/devops/profiles/prod/services_list.txt b/TOOLS/config/test/services_list.txt similarity index 100% rename from devops/profiles/prod/services_list.txt rename to TOOLS/config/test/services_list.txt diff --git a/TOOLS/docs-generator/main.go b/TOOLS/docs-generator/main.go index a36589f..7191f24 100644 --- a/TOOLS/docs-generator/main.go +++ b/TOOLS/docs-generator/main.go @@ -22,7 +22,7 @@ import ( func main() { resourcesDirFlag := flag.String("resources", "", "Path to resources_yaml") docsDirFlag := flag.String("docs", "", "Path to docs/30_registry/resources") - servicesListFlag := flag.String("services", "", "Path to devops/config/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") versionFlag := flag.String("version", "", "Provider version for example block") apiEndpointFlag := flag.String("api-endpoint", "https://deck-api.ngcloud.ru/api/v1/index.cfm", "API endpoint for example block") diff --git a/TOOLS/resource-generator/main.go b/TOOLS/resource-generator/main.go index b3aad42..c500c2b 100644 --- a/TOOLS/resource-generator/main.go +++ b/TOOLS/resource-generator/main.go @@ -1,6 +1,6 @@ // generate_resources_v2 — главный генератор Terraform-провайдера Nubes. // -// ⛔ ПОДЧИНЯЕТСЯ devops/ARCHITECTURE.md — все правила генерации прописаны там. +// ⛔ ПОДЧИНЯЕТСЯ TOOLS/ARCHITECTURE.md — все правила генерации прописаны там. // // Пайплайн: YAML-спеки сервисов → Go-ресурсы + registry.go. // @@ -17,7 +17,6 @@ package main import ( "os" - "path/filepath" "strings" "resource-generator/internal/loader" @@ -25,17 +24,13 @@ import ( ) func main() { - root, err := os.Getwd() - if err != nil { - panic(err) - } resourcesDir := strings.TrimSpace(os.Getenv("NUBES_RESOURCES_DIR")) if resourcesDir == "" { - resourcesDir = filepath.Join(root, "resources_yaml") + panic("NUBES_RESOURCES_DIR is required — must point to generated/{stand}/resources_yaml/") } outDir := strings.TrimSpace(os.Getenv("NUBES_RESOURCES_GEN_DIR")) if outDir == "" { - outDir = filepath.Join(root, "internal", "resources_gen") + panic("NUBES_RESOURCES_GEN_DIR is required — must point to generated/{stand}/go/") } instanceResources, subresources, actions, err := loader.LoadSpecs(resourcesDir) diff --git a/devops/00_token_manager.sh b/TOOLS/scripts/00_token_manager.sh similarity index 99% rename from devops/00_token_manager.sh rename to TOOLS/scripts/00_token_manager.sh index 0d88a5a..a925e46 100755 --- a/devops/00_token_manager.sh +++ b/TOOLS/scripts/00_token_manager.sh @@ -2,7 +2,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -DEFAULT_BASE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +DEFAULT_BASE_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)" BASE_DIR="${BASE_DIR:-${DEFAULT_BASE_DIR}}" CLIENT_ID="${CLIENT_ID:-account-console}" TOKEN_URL="${TOKEN_URL:-https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/token}" diff --git a/devops/01_generate_yamls.sh b/TOOLS/scripts/01_generate_yamls.sh similarity index 97% rename from devops/01_generate_yamls.sh rename to TOOLS/scripts/01_generate_yamls.sh index a41d114..b542994 100755 --- a/devops/01_generate_yamls.sh +++ b/TOOLS/scripts/01_generate_yamls.sh @@ -2,7 +2,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" PROVIDER_DIR="${ROOT_DIR}/provider" PROFILE_DIR="${PROFILE_DIR:-}" @@ -43,7 +43,7 @@ resolve_root_path() { echo "${ROOT_DIR}/${path_value}" } -SERVICES_FILE_DEFAULT="${ROOT_DIR}/devops/config/services_list.txt" +SERVICES_FILE_DEFAULT="${PROFILE_DIR}/services_list.txt" if [[ -f "${PROFILE_DIR}/services_list.txt" ]]; then SERVICES_FILE_DEFAULT="${PROFILE_DIR}/services_list.txt" fi @@ -68,7 +68,7 @@ fi # - Формат имени файла: ID_имя.yaml (например, 13_s3bucket.yaml). # # Источники данных: -# - Список сервисов: ${ROOT_DIR}/devops/config/services_list.txt +# - Список сервисов: TOOLS/config/{stand}/services_list.txt # - API endpoint: ${API_ENDPOINT} # - Токен: переменная NUBES_API_TOKEN или файл .token. # @@ -109,7 +109,7 @@ API_ENDPOINT="${NUBES_API_ENDPOINT:-https://deck-api.ngcloud.ru/api/v1/index.cfm # Auto-detect API style: if endpoint contains "index.cfm" → legacy proxy (?endpoint=), # otherwise → new REST gateway (direct paths). No forced /index.cfm normalization. -GENERATED_DIR="${PROFILE_DIR}/generated" +GENERATED_DIR="${ROOT_DIR}/generated/$(basename "$PROFILE_DIR")" YAML_OUTPUT_DIR_DEFAULT="${GENERATED_DIR}/resources_yaml" FAILURES_FILE_DEFAULT="${GENERATED_DIR}/tmp/yaml_gen_failures.txt" diff --git a/devops/02_generate_resources_and_docs_template_v2.sh b/TOOLS/scripts/02_generate_resources_and_docs_template_v2.sh similarity index 82% rename from devops/02_generate_resources_and_docs_template_v2.sh rename to TOOLS/scripts/02_generate_resources_and_docs_template_v2.sh index 3e86dad..e2022ce 100755 --- a/devops/02_generate_resources_and_docs_template_v2.sh +++ b/TOOLS/scripts/02_generate_resources_and_docs_template_v2.sh @@ -5,11 +5,11 @@ set -euo pipefail # Canonical docs generator for unified resources_yaml. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" PROVIDER_DIR="${ROOT_DIR}/provider" RESOURCES_YAML_DIR="${PROVIDER_DIR}/resources_yaml" DOCS_DIR="${ROOT_DIR}/docs/30_registry/resources" -SERVICES_LIST_PATH="${ROOT_DIR}/devops/config/services_list.txt" +SERVICES_LIST_PATH="${PROFILE_DIR}/services_list.txt" cd "$PROVIDER_DIR" diff --git a/devops/02_generate_resources_and_docs_v2.sh b/TOOLS/scripts/02_generate_resources_and_docs_v2.sh similarity index 91% rename from devops/02_generate_resources_and_docs_v2.sh rename to TOOLS/scripts/02_generate_resources_and_docs_v2.sh index 13a39bc..963ebf9 100755 --- a/devops/02_generate_resources_and_docs_v2.sh +++ b/TOOLS/scripts/02_generate_resources_and_docs_v2.sh @@ -5,7 +5,7 @@ set -euo pipefail # Restores instruction-style docs structure (manual/example/params_create/params_modify/outputs/ops/params landing). SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" PROVIDER_DIR="${ROOT_DIR}/provider" PROFILE_DIR="${PROFILE_DIR:-}" @@ -48,10 +48,10 @@ fi PROFILE_DIR="$(resolve_root_path "$PROFILE_DIR")" -RESOURCES_YAML_DIR_DEFAULT="${PROFILE_DIR}/generated/resources_yaml" -DOCS_DIR_DEFAULT="${PROFILE_DIR}/generated/docs" +RESOURCES_YAML_DIR_DEFAULT="${ROOT_DIR}/generated/$(basename "$PROFILE_DIR")/resources_yaml" +DOCS_DIR_DEFAULT="${ROOT_DIR}/generated/$(basename "$PROFILE_DIR")/docs" SERVICES_LIST_PATH_DEFAULT="${PROFILE_DIR}/services_list.txt" -GO_OUTPUT_DIR_DEFAULT="${PROFILE_DIR}/generated/go" +GO_OUTPUT_DIR_DEFAULT="${ROOT_DIR}/generated/$(basename "$PROFILE_DIR")/go" RESOURCES_YAML_DIR="${RESOURCES_YAML_DIR:-$RESOURCES_YAML_DIR_DEFAULT}" DOCS_DIR="${DOCS_DIR:-$DOCS_DIR_DEFAULT}" diff --git a/devops/03_build_and_upload_provider.sh b/TOOLS/scripts/03_build_and_upload_provider.sh similarity index 93% rename from devops/03_build_and_upload_provider.sh rename to TOOLS/scripts/03_build_and_upload_provider.sh index 3d2df4d..6fcfcaa 100755 --- a/devops/03_build_and_upload_provider.sh +++ b/TOOLS/scripts/03_build_and_upload_provider.sh @@ -2,9 +2,9 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" PROVIDER_MAIN="${ROOT_DIR}/provider/main.go" -BUILD_SCRIPT="${ROOT_DIR}/devops/build-provider.sh" +BUILD_SCRIPT="${ROOT_DIR}/TOOLS/scripts/build-provider.sh" PROFILE_DIR="${PROFILE_DIR:-}" if [[ "${1:-}" == "--profile" ]]; then @@ -45,10 +45,10 @@ resolve_root_path() { } S3CFG_REGISTRY="${S3CFG_REGISTRY:-${ROOT_DIR}/secrets/.s3cfg_registry}" -TIMEOUTS_SOURCE="${TIMEOUTS_SOURCE:-${ROOT_DIR}/devops/config/operation_timeouts.json}" -PROFILE_RESOURCES_YAML_DIR="${PROFILE_DIR}/generated/resources_yaml" -PROFILE_GENERATED_GO_DIR="${PROFILE_DIR}/generated/go" -PROFILE_BUILD_DIR="${PROFILE_DIR}/generated/provider_build" +TIMEOUTS_SOURCE="${TIMEOUTS_SOURCE:-${PROFILE_DIR}/operation_timeouts.json}" +PROFILE_RESOURCES_YAML_DIR="${ROOT_DIR}/generated/$(basename "$PROFILE_DIR")/resources_yaml" +PROFILE_GENERATED_GO_DIR="${ROOT_DIR}/generated/$(basename "$PROFILE_DIR")/go" +PROFILE_BUILD_DIR="${ROOT_DIR}/generated/$(basename "$PROFILE_DIR")/provider_build" S3CFG_REGISTRY="$(resolve_root_path "$S3CFG_REGISTRY")" TIMEOUTS_SOURCE="$(resolve_root_path "$TIMEOUTS_SOURCE")" PROFILE_RESOURCES_YAML_DIR="$(resolve_root_path "$PROFILE_RESOURCES_YAML_DIR")" diff --git a/devops/04_build_and_publish_docs.sh b/TOOLS/scripts/04_build_and_publish_docs.sh similarity index 98% rename from devops/04_build_and_publish_docs.sh rename to TOOLS/scripts/04_build_and_publish_docs.sh index 68428dc..dee6996 100755 --- a/devops/04_build_and_publish_docs.sh +++ b/TOOLS/scripts/04_build_and_publish_docs.sh @@ -2,7 +2,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" PROVIDER_MAIN="${ROOT_DIR}/provider/main.go" PROFILE_DIR="${PROFILE_DIR:-}" @@ -74,7 +74,7 @@ TMP_DOCS_DIR="" if [[ -n "$PROFILE_DIR" ]]; then # ⛔ NEVER merge with docs/ — ONLY generated docs from docs_gen// - DOCS_GEN_DIR="${DOCS_GEN_DIR:-provider/docs_gen/test}" + DOCS_GEN_DIR="${DOCS_GEN_DIR:-generated/test}" DOCS_GEN_DIR="$(resolve_root_path "$DOCS_GEN_DIR")" if [[ -d "$DOCS_GEN_DIR" ]]; then export MKDOCS_DOCS_DIR="$DOCS_GEN_DIR" diff --git a/devops/10_yaml_stability_run.sh b/TOOLS/scripts/10_yaml_stability_run.sh similarity index 90% rename from devops/10_yaml_stability_run.sh rename to TOOLS/scripts/10_yaml_stability_run.sh index a427156..7416838 100755 --- a/devops/10_yaml_stability_run.sh +++ b/TOOLS/scripts/10_yaml_stability_run.sh @@ -2,8 +2,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -SCRIPT="${ROOT_DIR}/devops/01_generate_yamls.sh" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" +SCRIPT="${ROOT_DIR}/TOOLS/scripts/01_generate_yamls.sh" TOKEN_FILE="${TOKEN_FILE:-}" REQUEST_DELAY="${REQUEST_DELAY:-0.2}" ATTEMPTS="${ATTEMPTS:-3}" diff --git a/devops/11_yaml_stability_run_latest.sh b/TOOLS/scripts/11_yaml_stability_run_latest.sh similarity index 76% rename from devops/11_yaml_stability_run_latest.sh rename to TOOLS/scripts/11_yaml_stability_run_latest.sh index a0fce12..4d0eba8 100755 --- a/devops/11_yaml_stability_run_latest.sh +++ b/TOOLS/scripts/11_yaml_stability_run_latest.sh @@ -2,7 +2,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" TOKEN_FILE="${TOKEN_FILE:-}" if [[ -z "$TOKEN_FILE" ]]; then @@ -15,4 +15,4 @@ if [[ -z "$TOKEN_FILE" || ! -f "$TOKEN_FILE" ]]; then fi export TOKEN_FILE -exec "${ROOT_DIR}/devops/10_yaml_stability_run.sh" +exec "${ROOT_DIR}/TOOLS/scripts/10_yaml_stability_run.sh" diff --git a/devops/12_generate_yamls_latest.sh b/TOOLS/scripts/12_generate_yamls_latest.sh similarity index 77% rename from devops/12_generate_yamls_latest.sh rename to TOOLS/scripts/12_generate_yamls_latest.sh index 95ccbbd..65dc27b 100755 --- a/devops/12_generate_yamls_latest.sh +++ b/TOOLS/scripts/12_generate_yamls_latest.sh @@ -2,7 +2,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" TOKEN_FILE="${TOKEN_FILE:-}" if [[ -z "$TOKEN_FILE" ]]; then @@ -15,4 +15,4 @@ if [[ -z "$TOKEN_FILE" || ! -f "$TOKEN_FILE" ]]; then fi export TOKEN_FILE -exec "${ROOT_DIR}/devops/01_generate_yamls.sh" +exec "${ROOT_DIR}/TOOLS/scripts/01_generate_yamls.sh" diff --git a/devops/13_generate_yamls_clean.sh b/TOOLS/scripts/13_generate_yamls_clean.sh similarity index 75% rename from devops/13_generate_yamls_clean.sh rename to TOOLS/scripts/13_generate_yamls_clean.sh index 826d51b..77703da 100755 --- a/devops/13_generate_yamls_clean.sh +++ b/TOOLS/scripts/13_generate_yamls_clean.sh @@ -2,7 +2,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" PROVIDER_DIR="${ROOT_DIR}/provider" if [[ ! -d "${PROVIDER_DIR}/resources_yaml" ]]; then @@ -13,4 +13,4 @@ fi # Start from a clean slate to avoid stale YAMLs. rm -f "${PROVIDER_DIR}/resources_yaml"/*.yaml -exec "${ROOT_DIR}/devops/12_generate_yamls_latest.sh" +exec "${ROOT_DIR}/TOOLS/scripts/12_generate_yamls_latest.sh" diff --git a/devops/build-provider.sh b/TOOLS/scripts/build-provider.sh similarity index 98% rename from devops/build-provider.sh rename to TOOLS/scripts/build-provider.sh index 9099aca..387843c 100755 --- a/devops/build-provider.sh +++ b/TOOLS/scripts/build-provider.sh @@ -28,7 +28,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" +ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}" PROVIDER_DIR="${PROVIDER_DIR:-${ROOT_DIR}/provider}" BUILD_DIR="${BUILD_DIR:-${ROOT_DIR}/artifacts/provider_build}" GPG_KEY_FILE="${GPG_KEY_FILE:-${ROOT_DIR}/secrets/private_key.asc}" diff --git a/TOOLS/yaml-generator/main.go b/TOOLS/yaml-generator/main.go index bde56a9..cce499f 100644 --- a/TOOLS/yaml-generator/main.go +++ b/TOOLS/yaml-generator/main.go @@ -34,11 +34,7 @@ func main() { outputDir := cfg.OutputDir if outputDir == "" { - repoRoot, err := config.FindRepoRoot() - if err != nil { - panic(err) - } - outputDir = filepath.Join(repoRoot, "resources_yaml") + panic("NUBES_OUTPUT_DIR is required — generated files must go to generated/{stand}/resources_yaml/") } if err := spec.ValidateOutputDir(outputDir); err != nil { panic(err) diff --git a/devops/04b_enrich_outputs_from_inventory.py b/devops/04b_enrich_outputs_from_inventory.py deleted file mode 100644 index 0d88564..0000000 --- a/devops/04b_enrich_outputs_from_inventory.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env python3 -""" -Дополнительный генератор: обогащает *_outputs.md реальными ключами -из output_inventory (собранного со стендов). - -Обновляет только секцию "## Реальные поля из облака" — не трогает остальное. - -Запуск (из корня репо на VM): - python3 devops/04b_enrich_outputs_from_inventory.py [--dry-run] -""" - -import json, yaml, glob, os, sys - -BASE = "/home/naeel/terra/terraform" -YAML_DIR = f"{BASE}/devops/profiles/test/generated/resources_yaml" -INV_DIR = f"{BASE}/devops/profiles/test/generated/output_inventory" -DOCS_DIR = f"{BASE}/docs/30_registry/resources" - -SECTION_MARKER = "## Реальные поля из облака" - - -def flatten(obj, prefix=""): - """Рекурсивно собирает dot-notation ключи из вложенного dict/list.""" - keys = [] - if isinstance(obj, dict): - for k, v in obj.items(): - full = f"{prefix}.{k}" if prefix else k - keys.append(full) - if isinstance(v, (dict, list)): - keys.extend(flatten(v, full)) - elif isinstance(obj, list): - for i, v in enumerate(obj): - full = f"{prefix}.{i}" if prefix else str(i) - if isinstance(v, (dict, list)): - keys.extend(flatten(v, full)) - # скалярные элементы списка не добавляем — нет смысла - return keys - - -def best_instance(instances): - """Выбираем лучший инстанс: running с данными > suspended с данными.""" - candidates = [i for i in instances if i.get("out") or i.get("params")] - if not candidates: - return None - running = [i for i in candidates if i.get("status") == "running"] - return running[-1] if running else candidates[-1] - - -def build_section(inst): - params = inst.get("params") or {} - out = inst.get("out") or {} - vault = inst.get("vault_keys") or [] - source = inst.get("source", "test") - name = inst.get("name", "?") - status = inst.get("status", "?") - - lines = [ - f"{SECTION_MARKER} ({source} / {name} / {status})\n", - "\n", - "Поля ниже получены из реальных инстансов этого сервиса в облаке.\n", - "Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`.\n", - ] - - if params: - lines.append("\n### `state_params` ключи\n\n") - for k in params.keys(): - lines.append(f'- `state_params["{k}"]`\n') - - flat_out = flatten(out) - if flat_out: - lines.append("\n### `state_out_flat` ключи\n\n") - for k in flat_out: - lines.append(f'- `state_out_flat["{k}"]`\n') - - if vault: - lines.append("\n### `vault_secrets` ключи\n\n") - for k in vault: - lines.append(f'- `vault_secrets["{k}"]`\n') - - return "".join(lines) - - -def main(): - dry_run = "--dry-run" in sys.argv - - # 1. service_id → name (из YAML) - svc_to_name = {} - for f in glob.glob(f"{YAML_DIR}/*.yaml"): - try: - d = yaml.safe_load(open(f)) - sid = d.get("service_id") - name = d.get("name") or d.get("service_short_name") - if sid and name: - svc_to_name[sid] = name - except Exception: - pass - - updated = 0 - skipped = 0 - - for inv_file in sorted(glob.glob(f"{INV_DIR}/svc_*.json")): - if "index" in inv_file: - continue - - inv = json.load(open(inv_file)) - svc_id = inv.get("serviceId") - inst = best_instance(inv.get("instances", [])) - - if not inst: - print(f" skip svc={svc_id}: нет данных") - skipped += 1 - continue - - name = svc_to_name.get(svc_id) - if not name: - print(f" skip svc={svc_id}: нет YAML маппинга") - skipped += 1 - continue - - out_path = f"{DOCS_DIR}/{name}_outputs.md" - if not os.path.exists(out_path): - print(f" skip svc={svc_id} ({name}): нет файла {name}_outputs.md") - skipped += 1 - continue - - new_section = build_section(inst) - content = open(out_path).read() - - if SECTION_MARKER in content: - idx = content.index(SECTION_MARKER) - new_content = content[:idx] + new_section - else: - new_content = content.rstrip("\n") + "\n\n" + new_section - - if dry_run: - print(f" dry-run svc={svc_id} ({name}): {out_path}") - print(f" params={list((inst.get('params') or {}).keys())}") - print(f" out_flat_count={len(flatten(inst.get('out') or {}))}") - print(f" vault={inst.get('vault_keys', [])}") - else: - with open(out_path, "w") as f: - f.write(new_content) - print(f" updated svc={svc_id} ({name})") - - updated += 1 - - print(f"\nДone: {updated} updated, {skipped} skipped") - - -if __name__ == "__main__": - main() diff --git a/devops/README.md b/devops/README.md deleted file mode 100644 index 757e8ee..0000000 --- a/devops/README.md +++ /dev/null @@ -1,246 +0,0 @@ -# DevOps — Pipeline сборки Terraform-провайдера Nubes - -## Обзор - -``` -API Nubes → YAML → Go-ресурсы → Провайдер → S3 Registry - ↘ Markdown-доки → MkDocs-сайт -``` - -## Инструменты (TOOLS/) - -| Бинарник | Исходники | Назначение | -|---|---|---| -| `TOOLS/bin/yaml-generator` | `TOOLS/yaml-generator/` | API → `resources_yaml/*.yaml` | -| `TOOLS/bin/resource-generator` | `TOOLS/resource-generator/` | YAML → `internal/resources_gen/*.go` | -| `TOOLS/bin/docs-generator` | `TOOLS/docs-generator/` | YAML → Markdown-документация | - -Каждый инструмент — независимый Go-модуль со своим `go.mod`. - -## Скрипты пайплайна - -| # | Скрипт | Что делает | -|---|---|---| -| 00 | `00_token_manager.sh` | Получение/обновление API-токенов | -| 01 | `01_generate_yamls.sh` | API → YAML (требует `--profile`) | -| 02 | `02_generate_resources_and_docs_v2.sh` | YAML → Go + Docs (требует `--profile`) | -| 03 | `03_build_and_upload_provider.sh` | Сборка провайдера → GPG → S3 | -| 04 | `04_build_and_publish_docs.sh` | MkDocs-сайт → публикация | -| — | `10_yaml_stability_run.sh` | Тест стабильности (N запусков 01) | -| — | `11_yaml_stability_run_latest.sh` | Авто-токен → 10 | -| — | `12_generate_yamls_latest.sh` | Авто-токен → 01 | -| — | `13_generate_yamls_clean.sh` | Очистка YAML + авто-токен → 01 | -| — | `build-provider.sh` | Быстрая локальная сборка (без S3) | - -## Профили стендов - -``` -devops/profiles/ -├── test/profile.env # lk-api-gateway-test.ngcloud.ru -├── prod/profile.env # продакшн -└── dev/profile.env # дев-стенд -``` - -Каждый профиль: свой endpoint, токен, версия, S3-бакет, GPG-ключи. - -## Полный пайплайн (пример для test-стенда) - -```bash -# 1. Токен -./devops/00_token_manager.sh --profile devops/profiles/test - -# 2. YAML из API -./devops/01_generate_yamls.sh --profile devops/profiles/test - -# 3. Go-ресурсы + документация -./devops/02_generate_resources_and_docs_v2.sh --profile devops/profiles/test - -# 4. Сборка и загрузка провайдера -./devops/03_build_and_upload_provider.sh --profile devops/profiles/test - -# 5. Публикация документации -./devops/04_build_and_publish_docs.sh --profile devops/profiles/test -``` - -## Структура проекта - -``` -tf_provider/ -├── TOOLS/ # Генераторы (независимые Go-модули) -│ ├── yaml-generator/ -│ ├── resource-generator/ -│ ├── docs-generator/ -│ └── bin/ # Скомпилированные бинарники -├── provider/ # Terraform-провайдер (Go-модуль) -│ ├── internal/ -│ │ ├── core/ # HTTP-клиент API -│ │ ├── provider/ # Регистрация провайдера -│ │ ├── resources_core/ # Общая логика CRUD -│ │ └── resources_gen/ # Сгенерированные ресурсы -│ └── main.go -└── devops/ # Скрипты пайплайна + профили - ├── profiles/ - └── config/ -``` - -## Архитектура - -Подробно: `devops/ARCHITECTURE.md`. - -Outputs: -- YAML files in `universal_rebuild/resources_yaml` (file names use `ID_name.yaml`) - -Note: -- Every run regenerates all YAML specs from the API for the services listed in `services_list.txt`. -- This overwrites previous YAMLs and ensures resources/docs match the current API for that list. -- Before generation, the scripts clean old YAML and generated Go files, so each run starts from a clean slate. - -## Step 2: Generate Go resources and docs - -Script: `02_generate_resources_and_docs_v2.sh` - -Example: -```bash -./devops/02_generate_resources_and_docs_v2.sh -``` - -Outputs: -- Go files in `universal_rebuild/internal/resources_gen` -- Docs in `docs/30_registry/resources` - -## Step 3: Build and upload provider - -Script: `03_build_and_upload_provider.sh` - -Uses `registry-server-build/build-provider.sh` and signs with: -- `secrets/private_key.asc` (ignored by git) - -Example: -```bash -export S3_ENDPOINT=https://s3.msk-1.ngcloud.ru -export S3_ACCESS_KEY=... -export S3_SECRET_KEY=... -./devops/03_build_and_upload_provider.sh 2.0.2 -``` - -## Step 4: Build and publish docs - -Script: `04_build_and_publish_docs.sh` - -Example: -```bash -export S3_ENDPOINT=https://s3.msk-1.ngcloud.ru -export S3_ACCESS_KEY=... -export S3_SECRET_KEY=... -./devops/04_build_and_publish_docs.sh 2.0.2 -``` - -## Notes - -- The GPG private key must remain stable across releases. Do not regenerate per build. -- If the key is regenerated, the registry server must be updated to serve the new public key. -- Terraform will fail with `authentication signature from unknown issuer` if the registry public key does not match the signing key. -- `config/services_list.txt` is the source of truth for which services are generated. -- If the provider version changes, update `universal_rebuild/main.go`. - -## Quickstart (fresh run) - -```bash -# 0) Prepare token -export TOKEN_FILE=/HH-MM-SS.token - -# 1) Generate YAML specs from API -./devops/01_generate_yamls.sh - -# 2) Generate Go resources + docs -./devops/02_generate_resources_and_docs_v2.sh - -# 3) Build + upload provider -./devops/03_build_and_upload_provider.sh 2.0.2 - -# 4) Build + publish docs -./devops/04_build_and_publish_docs.sh 2.0.2 -``` - -## Troubleshooting checklist - -- If YAML generation fails: check token, API endpoint, and `/tmp/yaml_gen_failures.txt`. -- If docs are missing: run `02_generate_resources_and_docs_v2.sh`. -- If registry rejects artifacts: verify GPG key and public key in registry server. - -## One-time GPG bootstrap (do this once, keep the key stable) - -1) Generate and export keys (no passphrase): -```bash -GPG_DIR=/secrets -GNUPGHOME=$(mktemp -d) -cat > /tmp/gpg_batch <<'EOF' -%no-protection -Key-Type: RSA -Key-Length: 4096 -Subkey-Type: RSA -Subkey-Length: 4096 -Name-Real: tazet@narod.ru -Name-Email: tazet@narod.ru -Expire-Date: 0 -EOF -gpg --batch --homedir "$GNUPGHOME" --gen-key /tmp/gpg_batch -gpg --batch --homedir "$GNUPGHOME" --armor --export-secret-keys > "$GPG_DIR/private_key.asc" -gpg --batch --homedir "$GNUPGHOME" --armor --export > "$GPG_DIR/public_key.asc" -rm -rf "$GNUPGHOME" /tmp/gpg_batch -``` - -2) Update registry server public key (ASCII Armor) in: -- `registry-server-build/main.go` -- `operator/cmd/registry/main.go` - -3) Rebuild and redeploy the registry server (see `docs/50_history/00_system_mechanics.md`). - -4) Build and upload provider artifacts as usual. - -## Token and self-run shortcuts - -Token capture (JSON from F12): -```bash -cat token.json | ./devops/00_token_manager.sh save - -``` - -Use latest token automatically: -```bash -./devops/12_generate_yamls_latest.sh -``` - -10-run stability test (latest token): -```bash -REQUEST_DELAY=0.3 ATTEMPTS=3 ./devops/11_yaml_stability_run_latest.sh -``` - -Log output: -```bash -tail -n 200 /tmp/yaml_gen_runs.log -``` - -## Legacy (deprecated) - -- Ops YAML and ops-doc generators were removed after the unified YAML migration. - -## Docs build policy (updated) - -- `mkdocs`/`mkdocs-material` are **not** installed on every docs generation run. -- Preferred path: build docs in Docker (`squidfunk/mkdocs-material`) via `04_build_and_publish_docs.sh`. -- Fallback path: local `mkdocs` is used only if it is already installed. -- If local `mkdocs` is missing, install once in persistent virtualenv and reuse. - -One-time setup example: - -```bash -python3 -m venv .venv -.venv/bin/pip install mkdocs mkdocs-material -``` - -Then run docs publish normally: - -```bash -./devops/04_build_and_publish_docs.sh -``` - diff --git a/devops/ci/pipeline.yaml b/devops/ci/pipeline.yaml deleted file mode 100644 index a683ff6..0000000 --- a/devops/ci/pipeline.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# GitLab CI - pipeline для сборки и публикации Terraform Provider Registry -stages: - - test - - build - - sign - - publish - -variables: - HARBOR_HOST: "pearlharbor.registryk8s.services.ngcloud.ru" - S3_BUCKET: "terraform-registry" - PROVIDER_NAME: "nubes" - PROVIDER_NAMESPACE: "nubes" - -test: - stage: test - image: golang:1.24 - script: - - cd universal_rebuild - - go test ./... - - go vet ./... - -build-provider: - stage: build - image: golang:1.24 - script: - - cd universal_rebuild - - GOOS=linux GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_linux_amd64 - - GOOS=darwin GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_darwin_amd64 - - GOOS=windows GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_windows_amd64.exe - artifacts: - paths: [universal_rebuild/bin/] - -build-images: - stage: build - script: - - docker build -t ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG} registry-server-build/ - - docker build -t ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG} operator/ - - docker push ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG} - - docker push ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG} - only: - - tags - -sign: - stage: sign - image: golang:1.24 - script: - - cd universal_rebuild/bin - - sha256sum terraform-provider-* > SHA256SUMS - - gpg --import $GPG_PRIVATE_KEY - - gpg --detach-sign SHA256SUMS - artifacts: - paths: - - universal_rebuild/bin/SHA256SUMS - - universal_rebuild/bin/SHA256SUMS.gpg - -publish-to-s3: - stage: publish - image: alpine:3.19 - before_script: - - apk add --no-cache s3cmd - script: - - s3cmd put universal_rebuild/bin/* s3://${S3_BUCKET}/terraform-providers/ - only: - - tags diff --git a/devops/profiles/test/operation_timeouts.json b/devops/profiles/test/operation_timeouts.json deleted file mode 100644 index cbf0baa..0000000 --- a/devops/profiles/test/operation_timeouts.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "default_timeout": "10m", - "idle_timeout": "10m", - "operations": { - "create": "10m", - "modify": "7m", - "resume": "7m", - "suspend": "7m", - "create_user": "7m", - "delete_user": "6m", - "create_database": "7m", - "delete_database": "6m" - }, - "overrides": { - "services.90.create": "7m" - } -} diff --git a/devops/profiles/test/services_list.txt b/devops/profiles/test/services_list.txt deleted file mode 100644 index 5706525..0000000 --- a/devops/profiles/test/services_list.txt +++ /dev/null @@ -1,46 +0,0 @@ -# service_id service_name # RU description -# do not delete lines; to exclude a service, comment it with # -1 dummy # Болванка для тестов -2 template # Болванка для тестирования -12 s3 # Масштабируемое хранилище данных -13 s3bucket # Бакет в сервисе S3 -19 vcOrg # Организация в Cloud Director -20 vcOrgSaas # Без интеграции Keycloak -21 vc_vdc # Выделение вычислительных ресурсов -22 vc_nsxt # Обеспечение сетевой доступности в тенанте -23 vc_vm # Виртуальная машина без каких-либо особенностей.
Данная услуга запускает vc_ip для выдачи белого адреса и подключения к ВМ -# 24 DEPRECATED Правила маршрутизации для VM (vc_nat) # excluded from active generation -25 vcexternalip # Резервирование IP адреса -26 vapp # Группировка виртуальных машин -27 vc_vm_v2 # Виртуальная машина с возможностью настройки Cloud Init
Можно развернуть в услуге несколько ВМ
В зависимости от настроек можно выдавать на ВМ vip + dns -28 vc_vm_v3 # Создание виртуальной машины -29 vcVdcGroup # Обеспечение сетевой доступности между датацентрами -32 vmpostgre # Сервис настройки PostgreSQL для виртуальной машины.
Требует наличия ВМ -50 nextcloud # Платформа совместной работы -81 superset # Сервер отчетов -82 harbor # Реестр для хранения образов контейнеров -88 k8sZitiController # Контроллер управления Client to service & service to service Zero trust network
-89 flask # Flask -90 postgres # Управляемая база данных PostgreSQL -91 redis # Управляемая база данных Redis -92 mongodb # Управляемая база данных MongoDB -93 rabbitmq # Сервер очередей -94 lucee # Cервер приложений -95 nodejs # Cервер приложений -96 pgadmin # Административный интерфейс для PostgreSQL -97 nodered # Low-code платформа для IoT -98 http # Простой HTTP контейнер -99 gitea # Сервис контроля версий -100 openwhisk # Добавление и удаления пользователя (учетной записи, тенанта) для работы с Openwhisk -110 dnszone # Хостинг DNS зоны -111 dnsrecord # DNS запись в созданной зоне -112 tenant # Организация доступов к мониторингу -113 vcComplex # Создание готовой иерархии ресурсов -114 GiteaComplex # Комплексная услуга из ["gittea", "postgres"] -115 mariadb # Управляемая база данных MySQL -116 kafka # Сервис создания кластера Apache Kafka -117 nifi # ETL-система -119 Akhq # Web интерфейс Akhq для работы с Apache Kafka -120 clickhouse # Управляемая база данных ClickHouse -149 valoTenant # Корпоративная система обмена и синхронизации файлов -150 k8sSthutrvalCluster # Платформа управления K8s
diff --git a/generated/dev/docs/.gitkeep b/generated/dev/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/dev/docs/akhq.md b/generated/dev/docs/akhq.md new file mode 100644 index 0000000..b160ca3 --- /dev/null +++ b/generated/dev/docs/akhq.md @@ -0,0 +1,13 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## MAN + +
+# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group'ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
--- +
diff --git a/generated/dev/docs/akhq_example.md b/generated/dev/docs/akhq_example.md new file mode 100644 index 0000000..259b48c --- /dev/null +++ b/generated/dev/docs/akhq_example.md @@ -0,0 +1,50 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Copy-ready manifest (`akhq_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_akhq" "baza" { + resource_realm = "TODO" # Выбрать платформу из списка + domain = "TODO" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + kafka_uid = "TODO" # Выбрать кластер из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_memory = 512 # Пример: `1024` + resource_cpu = 500 # Пример: `1000` + resource_instances = 1 +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_akhq.baza.state_params["имя_ключа"] +nubes_akhq.baza.state_out["имя_ключа"] +nubes_akhq.baza.state_params_flat["имя_ключа"] +nubes_akhq.baza.state_out_flat["имя_ключа"] +nubes_akhq.baza.vault_secrets["имя_ключа"] +nubes_akhq.baza.vault_url +nubes_akhq.baza.vault_user_path +nubes_akhq.baza.vault_fields +``` diff --git a/generated/dev/docs/akhq_ops.md b/generated/dev/docs/akhq_ops.md new file mode 100644 index 0000000..9e4364f --- /dev/null +++ b/generated/dev/docs/akhq_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](akhq_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](akhq_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/akhq_outputs.md b/generated/dev/docs/akhq_outputs.md new file mode 100644 index 0000000..22405f5 --- /dev/null +++ b/generated/dev/docs/akhq_outputs.md @@ -0,0 +1,33 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` + +### `vault_secrets` ключи + +- `vault_secrets["pass"]` +- `vault_secrets["user"]` + diff --git a/generated/dev/docs/akhq_params.md b/generated/dev/docs/akhq_params.md new file mode 100644 index 0000000..b419cf6 --- /dev/null +++ b/generated/dev/docs/akhq_params.md @@ -0,0 +1,12 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Parameters + +- [Create params](akhq_params_create.md) +- [Modify params](akhq_params_modify.md) diff --git a/generated/dev/docs/akhq_params_create.md b/generated/dev/docs/akhq_params_create.md new file mode 100644 index 0000000..687ca12 --- /dev/null +++ b/generated/dev/docs/akhq_params_create.md @@ -0,0 +1,40 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
550resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
553domainstringЕсли будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
555kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
551resource_memoryinteger512Пример: `1024`minvalue=512
552resource_cpuinteger500Пример: `1000`minvalue=500
554resource_instancesinteger1minvalue=1
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/akhq_params_modify.md b/generated/dev/docs/akhq_params_modify.md new file mode 100644 index 0000000..1ba2657 --- /dev/null +++ b/generated/dev/docs/akhq_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
565kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
diff --git a/generated/dev/docs/dnsrecord.md b/generated/dev/docs/dnsrecord.md new file mode 100644 index 0000000..22f9734 --- /dev/null +++ b/generated/dev/docs/dnsrecord.md @@ -0,0 +1,13 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## MAN + +
+Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи +
diff --git a/generated/dev/docs/dnsrecord_example.md b/generated/dev/docs/dnsrecord_example.md new file mode 100644 index 0000000..7ba7cc8 --- /dev/null +++ b/generated/dev/docs/dnsrecord_example.md @@ -0,0 +1,49 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Copy-ready manifest (`dnsrecord_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_dnsrecord" "baza" { + zone_uid = "TODO" # UUID экземпляра услуги DNS-зоны + record_type = "A" # Тип DNS-записи + record_name = "TODO" # Имя записи. Имя зоны в конце проставляется автоматически + record_input = "TODO" # Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + record_ttl = 3600 # Time-to-Leave записи. Раз в какое время запись будет обновлена +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dnsrecord.baza.state_params["имя_ключа"] +nubes_dnsrecord.baza.state_out["имя_ключа"] +nubes_dnsrecord.baza.state_params_flat["имя_ключа"] +nubes_dnsrecord.baza.state_out_flat["имя_ключа"] +nubes_dnsrecord.baza.vault_secrets["имя_ключа"] +nubes_dnsrecord.baza.vault_url +nubes_dnsrecord.baza.vault_user_path +nubes_dnsrecord.baza.vault_fields +``` diff --git a/generated/dev/docs/dnsrecord_ops.md b/generated/dev/docs/dnsrecord_ops.md new file mode 100644 index 0000000..475844c --- /dev/null +++ b/generated/dev/docs/dnsrecord_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dnsrecord_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](dnsrecord_params_modify.md) diff --git a/generated/dev/docs/dnsrecord_outputs.md b/generated/dev/docs/dnsrecord_outputs.md new file mode 100644 index 0000000..4d127c2 --- /dev/null +++ b/generated/dev/docs/dnsrecord_outputs.md @@ -0,0 +1,29 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dnsServers"]` +- `state_out_flat["record"]` + diff --git a/generated/dev/docs/dnsrecord_params.md b/generated/dev/docs/dnsrecord_params.md new file mode 100644 index 0000000..84701f9 --- /dev/null +++ b/generated/dev/docs/dnsrecord_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Parameters + +- [Create params](dnsrecord_params_create.md) +- [Modify params](dnsrecord_params_modify.md) diff --git a/generated/dev/docs/dnsrecord_params_create.md b/generated/dev/docs/dnsrecord_params_create.md new file mode 100644 index 0000000..b3a67ca --- /dev/null +++ b/generated/dev/docs/dnsrecord_params_create.md @@ -0,0 +1,27 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
75zone_uiduuidUUID экземпляра услуги DNS-зоныref_svc_id=110
76record_typestringТип DNS-записиvalue_list=A, AAAA, CNAME, MX, PTR, SOA, SRV, TXT, LUA, NS, SSHFP
77record_namestringИмя записи. Имя зоны в конце проставляется автоматическиunique_scope=parent
78record_inputstringЗначение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
450record_ttlinteger3600Time-to-Leave записи. Раз в какое время запись будет обновленаminvalue=30; maxvalue=604800
diff --git a/generated/dev/docs/dnsrecord_params_modify.md b/generated/dev/docs/dnsrecord_params_modify.md new file mode 100644 index 0000000..d3cdbdb --- /dev/null +++ b/generated/dev/docs/dnsrecord_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
256record_inputstringЗапись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP
diff --git a/generated/dev/docs/dnszone.md b/generated/dev/docs/dnszone.md new file mode 100644 index 0000000..f8249dd --- /dev/null +++ b/generated/dev/docs/dnszone.md @@ -0,0 +1,13 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## MAN + +
+## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
``` +
diff --git a/generated/dev/docs/dnszone_example.md b/generated/dev/docs/dnszone_example.md new file mode 100644 index 0000000..048e1e3 --- /dev/null +++ b/generated/dev/docs/dnszone_example.md @@ -0,0 +1,52 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Copy-ready manifest (`dnszone_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_dnszone" "baza" { + zone_email = "TODO" # Контактный e-mail + zone_name = "TODO" # Имя зоны + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + zone_kind = "NATIVE" # Тип зоны + zone_time_to_refresh = 86400 # Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов + zone_time_to_retry = 7200 # Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов + zone_time_to_expire = 3600000 # Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным + zone_time_to_live = 86400 # Время, в течение которого информация будет кешироваться другими DNS-серверами +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dnszone.baza.state_params["имя_ключа"] +nubes_dnszone.baza.state_out["имя_ключа"] +nubes_dnszone.baza.state_params_flat["имя_ключа"] +nubes_dnszone.baza.state_out_flat["имя_ключа"] +nubes_dnszone.baza.vault_secrets["имя_ключа"] +nubes_dnszone.baza.vault_url +nubes_dnszone.baza.vault_user_path +nubes_dnszone.baza.vault_fields +``` diff --git a/generated/dev/docs/dnszone_ops.md b/generated/dev/docs/dnszone_ops.md new file mode 100644 index 0000000..e07344c --- /dev/null +++ b/generated/dev/docs/dnszone_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dnszone_params_create.md) +- `delete` diff --git a/generated/dev/docs/dnszone_outputs.md b/generated/dev/docs/dnszone_outputs.md new file mode 100644 index 0000000..0c3556d --- /dev/null +++ b/generated/dev/docs/dnszone_outputs.md @@ -0,0 +1,32 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dnsServers"]` +- `state_out_flat["dnsServers.ns1.nubes.ru"]` +- `state_out_flat["dnsServers.ns2.nubes.ru"]` +- `state_out_flat["zoneSerial"]` +- `state_out_flat["zoneSoaServer"]` + diff --git a/generated/dev/docs/dnszone_params.md b/generated/dev/docs/dnszone_params.md new file mode 100644 index 0000000..0f1f739 --- /dev/null +++ b/generated/dev/docs/dnszone_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Parameters + +- [Create params](dnszone_params_create.md) +- [Modify params](dnszone_params_modify.md) diff --git a/generated/dev/docs/dnszone_params_create.md b/generated/dev/docs/dnszone_params_create.md new file mode 100644 index 0000000..0690a8f --- /dev/null +++ b/generated/dev/docs/dnszone_params_create.md @@ -0,0 +1,31 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
66zone_emailstringКонтактный e-mail
67zone_namestringИмя зоныregex=^([a-z0-9-]+\.)+[a-z0-9-]+$
69zone_masters_for_slavestringИспользовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов
284resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
68zone_kindstringNATIVEТип зоныvalue_list=NATIVE, SLAVE
70zone_time_to_refreshstring86400Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов
71zone_time_to_retrystring7200Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов
72zone_time_to_expirestring3600000Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным
73zone_time_to_livestring86400Время, в течение которого информация будет кешироваться другими DNS-серверами
diff --git a/generated/dev/docs/dnszone_params_modify.md b/generated/dev/docs/dnszone_params_modify.md new file mode 100644 index 0000000..bd1dc13 --- /dev/null +++ b/generated/dev/docs/dnszone_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/dummy.md b/generated/dev/docs/dummy.md new file mode 100644 index 0000000..53f9855 --- /dev/null +++ b/generated/dev/docs/dummy.md @@ -0,0 +1,13 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## MAN + +
+## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
``` +
diff --git a/generated/dev/docs/dummy_example.md b/generated/dev/docs/dummy_example.md new file mode 100644 index 0000000..4febf08 --- /dev/null +++ b/generated/dev/docs/dummy_example.md @@ -0,0 +1,49 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Copy-ready manifest (`dummy_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_dummy" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + duration_ms = 0 # перед завершением подождать (sleep) столько миллисекунд + fail_at_start = false # если true, завершить джоб, ничего не записывая в cmdb + fail_in_progress = false # если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + where_fail = 1 # принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dummy.baza.state_params["имя_ключа"] +nubes_dummy.baza.state_out["имя_ключа"] +nubes_dummy.baza.state_params_flat["имя_ключа"] +nubes_dummy.baza.state_out_flat["имя_ключа"] +nubes_dummy.baza.vault_secrets["имя_ключа"] +nubes_dummy.baza.vault_url +nubes_dummy.baza.vault_user_path +nubes_dummy.baza.vault_fields +``` diff --git a/generated/dev/docs/dummy_ops.md b/generated/dev/docs/dummy_ops.md new file mode 100644 index 0000000..3af1a55 --- /dev/null +++ b/generated/dev/docs/dummy_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dummy_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](dummy_params_modify.md) +- `reconcile` +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/dev/docs/dummy_outputs.md b/generated/dev/docs/dummy_outputs.md new file mode 100644 index 0000000..52b99e1 --- /dev/null +++ b/generated/dev/docs/dummy_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `vault_secrets` ключи + +- `vault_secrets["someKey"]` + diff --git a/generated/dev/docs/dummy_params.md b/generated/dev/docs/dummy_params.md new file mode 100644 index 0000000..67ae088 --- /dev/null +++ b/generated/dev/docs/dummy_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Parameters + +- [Create params](dummy_params_create.md) +- [Modify params](dummy_params_modify.md) diff --git a/generated/dev/docs/dummy_params_create.md b/generated/dev/docs/dummy_params_create.md new file mode 100644 index 0000000..a098d0b --- /dev/null +++ b/generated/dev/docs/dummy_params_create.md @@ -0,0 +1,46 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
242resource_realmstringНаименование ресурсной платформы для подключенияfunc=getAvailableResourceRealms
286bodymessagestringДата, уходящая в Vault
321map_examplemap
322json_examplejsonДля тестирования интерфейса. Сюда можно писать корректный json
396nested_ref_example
447yaml_exampleyaml
647map_fixedmap-fixedНовый тип поля
654array_map_fixed_examplearray-map-fixedArray of map-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
198duration_msinteger0перед завершением подождать (sleep) столько миллисекунд
199fail_at_startbooleanfalse**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой
value_list=false, true
200fail_in_progressbooleanfalseесли true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdbvalue_list=false, true
201where_failinteger1принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault writevalue_list=1, 2, 3
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/dummy_params_modify.md b/generated/dev/docs/dummy_params_modify.md new file mode 100644 index 0000000..b601221 --- /dev/null +++ b/generated/dev/docs/dummy_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
287duration_msintegerперед завершением подождать (sleep) столько миллисекунд
288fail_at_startbooleanесли true, завершить джоб, ничего не записывая в cmdbvalue_list=false, true
289fail_in_progressbooleanесли true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdbvalue_list=false, true
290where_failintegerпринимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault writevalue_list=1, 2, 3
291bodymessageДата, уходящая в Vault
373map_examplemap
374json_example
diff --git a/generated/dev/docs/flask.md b/generated/dev/docs/flask.md new file mode 100644 index 0000000..763135a --- /dev/null +++ b/generated/dev/docs/flask.md @@ -0,0 +1,13 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## MAN + +
+# Инструкция по развертыванию Web-сервер с фреймворком Flask через платформу

## 1. Общая информация

Сервис **Web-сервер с фреймворком Flask** предназначен для развертывания веб-приложений на базе Flask.

### Доступные операции

create — включить сервис, развернуть веб-приложение с указанными параметрами.
delete — удалить сервис, только после suspend.
modify — изменить настройки сервиса и переразвернуть.
suspend — выключить веб-приложение, остановить обслуживание.
resume — включить веб-приложение после suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Имя домена
Описание: доменное имя для доступа к веб-приложению. Если указан FQDN, происходит проверка DNS на валидность А-записи. А-запись должна указывать на балансировщик ресурсной платформы. Если указано имя в рамках одного октета, оно будет добавлено в публичную DNS-зону.
Пример значения: строка, допустимые FQDN или имя в одном октете. Пример: example.com или myapp.

### Платформа для развертывания
Описание: кластер Kubernetes, в котором будет развернуто приложение.
Пример значения: идентификатор кластера Kubernetes.

### Квота ядра пода
Описание: количество CPU, выделяемое на под, в Milicores.
Пример значения: целое число, например 500 для 0.5 ядра.

### Квота памяти пода
Описание: количество памяти, выделяемое на под, в Megabytes.
Пример значения: целое число, например 512 для 512 MB.

### Кол-во реплик пода
Описание: количество инстансов пода для масштабирования.
Пример значения: целое число, шт, например 2.

### URL Проекта (gitpath)
Описание: полный HTTPS путь до проекта в Git-репозитории.
Пример значения: URL в формате https://github.com/myuser/myproject.git. Доступ к репозиторию должен быть публичным.

### URL HealthCheck
Описание: путь для проверки здоровья приложения внутри пода.
Пример значения: строка, путь, например /api/v1/health.

### Переменные окружения пода
Описание: структура данных, которая будет передана как переменные окружения в под.
Пример значения: ключ-значение, например {"ENV": "production"}.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Квота ядра пода: 300m (0.3 ядра)
- Квота памяти пода: 256 MB
- Кол-во реплик пода: 1

### Промышленное окружение
- Квота ядра пода: от 1000m (1 ядро) в зависимости от нагрузки
- Квота памяти пода: от 1024 MB (1 GB) с учетом потребностей приложения
- Кол-во реплик пода: от 2 для обеспечения отказоустойчивости

---

## 4. Выходные параметры
Операция create не возвращает явных выходных параметров. Успешное выполнение операции приводит к развертыванию сервиса, доступного по указанному домену.

---

## 5. Дополнительная информация
- В корне проекта должен находиться файл requirements.txt со списком зависимостей Python.
- Исходный код веб-приложения должен располагаться в директории site.
- Запуск веб-сервера осуществляется командой python app.py.
- Используется версия Python: python:3.12-slim.
- Git-репозиторий должен быть публично доступен для платформы.
- Операция delete возможна только после suspend.
- Операция modify приводит к переразвертыванию с новыми параметрами. +
diff --git a/generated/dev/docs/flask_example.md b/generated/dev/docs/flask_example.md new file mode 100644 index 0000000..80de273 --- /dev/null +++ b/generated/dev/docs/flask_example.md @@ -0,0 +1,50 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Copy-ready manifest (`flask_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_flask" "baza" { + resource_realm = "TODO" # Кластер Kubernetes в котором будет развёрнуто приложение + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан `FQDN`, то происходят проверки DNS на валидность А-записи. А-запись **обязана смотреть на балансировщик** ресурсной платформы. Если указано имя `в рамках одного октета`, будет добавлено **в публичную днс-зону** + resource_cpu = 300 # Пример: `1000` + resource_memory = 256 # Пример: `2048` + resource_instances = 1 # Пример: `3` + git_path = "https://github.com/Foxyhhd/Baldurs-Gate-test.git" # Пример: `https://github.com/myuser/myproject.git` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_flask.baza.state_params["имя_ключа"] +nubes_flask.baza.state_out["имя_ключа"] +nubes_flask.baza.state_params_flat["имя_ключа"] +nubes_flask.baza.state_out_flat["имя_ключа"] +nubes_flask.baza.vault_secrets["имя_ключа"] +nubes_flask.baza.vault_url +nubes_flask.baza.vault_user_path +nubes_flask.baza.vault_fields +``` diff --git a/generated/dev/docs/flask_ops.md b/generated/dev/docs/flask_ops.md new file mode 100644 index 0000000..518dea6 --- /dev/null +++ b/generated/dev/docs/flask_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](flask_params_create.md) +- `delete` — Удаление возможно только после остановки сервиса через операцию `suspend` После выполнения операции необходимо выждать определенное кол-во времени +- `modify` — Изменение кластера. Параметры: [Modify params](flask_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/flask_outputs.md b/generated/dev/docs/flask_outputs.md new file mode 100644 index 0000000..3c2d51a --- /dev/null +++ b/generated/dev/docs/flask_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/flask_params.md b/generated/dev/docs/flask_params.md new file mode 100644 index 0000000..ca3f639 --- /dev/null +++ b/generated/dev/docs/flask_params.md @@ -0,0 +1,12 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Parameters + +- [Create params](flask_params_create.md) +- [Modify params](flask_params_modify.md) diff --git a/generated/dev/docs/flask_params_create.md b/generated/dev/docs/flask_params_create.md new file mode 100644 index 0000000..8227804 --- /dev/null +++ b/generated/dev/docs/flask_params_create.md @@ -0,0 +1,42 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
249resource_realmstringВыбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы
func=getAvailableResourceRealms
254json_envmapСтруктура, которая будет передана как переменные окружения
255health_pathstringМожет быть пустым. Если ничего не передано, проверяется `tcpsocket`
Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
248domainstringweb01Если будет указан `FQDN`, то происходят проверки DNS на валидность А-записи. А-запись **обязана смотреть на балансировщик** ресурсной платформы.
Если указано имя `в рамках одного октета`, будет добавлено **в публичную днс-зону**
regex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
250resource_cpuinteger300Указывается в Milicores
`1000` Mili == `1` Ядро
minvalue=100; maxvalue=8000
251resource_memoryinteger256Указывается в Megabytesminvalue=128; maxvalue=8192
252resource_instancesinteger1Балансировка производится между всеми репликамиminvalue=1; maxvalue=7
253git_pathstringhttps://github.com/Foxyhhd/Baldurs-Gate-test.gitПроект должен быть публичным
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/flask_params_modify.md b/generated/dev/docs/flask_params_modify.md new file mode 100644 index 0000000..1f5f44b --- /dev/null +++ b/generated/dev/docs/flask_params_modify.md @@ -0,0 +1,19 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Modify params + + + + + + + + + +
IDCodeTypeDescriptionConstraints
451resource_cpuintegerУказывается в Milicores
`1000` Mili == `1` Ядро
minvalue=100; maxvalue=8000
452resource_memoryintegerУказывается в Megabytesminvalue=128; maxvalue=8192
453resource_instancesintegerБалансировка производится между всеми репликамиminvalue=1; maxvalue=7
454git_pathПроект должен быть публичным
455json_envmapСтруктура, которая будет передана как переменные окружения
diff --git a/generated/dev/docs/gitea.md b/generated/dev/docs/gitea.md new file mode 100644 index 0000000..b0fabf5 --- /dev/null +++ b/generated/dev/docs/gitea.md @@ -0,0 +1,13 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## MAN + +
+Развертывает сервис контроля версий Gitea
Зависит от сервиса postgresql который должен быть развернут заранее. +
diff --git a/generated/dev/docs/gitea_example.md b/generated/dev/docs/gitea_example.md new file mode 100644 index 0000000..be105b4 --- /dev/null +++ b/generated/dev/docs/gitea_example.md @@ -0,0 +1,50 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Copy-ready manifest (`gitea_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_gitea" "baza" { + psql_uid = "TODO" # UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_gitea.baza.state_params["имя_ключа"] +nubes_gitea.baza.state_out["имя_ключа"] +nubes_gitea.baza.state_params_flat["имя_ключа"] +nubes_gitea.baza.state_out_flat["имя_ключа"] +nubes_gitea.baza.vault_secrets["имя_ключа"] +nubes_gitea.baza.vault_url +nubes_gitea.baza.vault_user_path +nubes_gitea.baza.vault_fields +``` diff --git a/generated/dev/docs/gitea_ops.md b/generated/dev/docs/gitea_ops.md new file mode 100644 index 0000000..0b22aac --- /dev/null +++ b/generated/dev/docs/gitea_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](gitea_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](gitea_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/gitea_outputs.md b/generated/dev/docs/gitea_outputs.md new file mode 100644 index 0000000..3944814 --- /dev/null +++ b/generated/dev/docs/gitea_outputs.md @@ -0,0 +1,39 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` + diff --git a/generated/dev/docs/gitea_params.md b/generated/dev/docs/gitea_params.md new file mode 100644 index 0000000..f0931ff --- /dev/null +++ b/generated/dev/docs/gitea_params.md @@ -0,0 +1,12 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Parameters + +- [Create params](gitea_params_create.md) +- [Modify params](gitea_params_modify.md) diff --git a/generated/dev/docs/gitea_params_create.md b/generated/dev/docs/gitea_params_create.md new file mode 100644 index 0000000..378430a --- /dev/null +++ b/generated/dev/docs/gitea_params_create.md @@ -0,0 +1,40 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
238psql_uiduuidUUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматическиref_svc_id=90
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
232resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=300; maxvalue=4000
233resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=256; maxvalue=8192
234resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
235resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
237domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/gitea_params_modify.md b/generated/dev/docs/gitea_params_modify.md new file mode 100644 index 0000000..f584096 --- /dev/null +++ b/generated/dev/docs/gitea_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
259resource_cpuintegerКвота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=500; maxvalue=4000
260resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=256; maxvalue=8192
261resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
262resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
diff --git a/generated/dev/docs/harbor.md b/generated/dev/docs/harbor.md new file mode 100644 index 0000000..c0cad0a --- /dev/null +++ b/generated/dev/docs/harbor.md @@ -0,0 +1,13 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## MAN + +
+Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak +
diff --git a/generated/dev/docs/harbor_example.md b/generated/dev/docs/harbor_example.md new file mode 100644 index 0000000..af9351a --- /dev/null +++ b/generated/dev/docs/harbor_example.md @@ -0,0 +1,51 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Copy-ready manifest (`harbor_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_harbor" "baza" { + resource_realm = "TODO" # Платформа для развертывания + s3_uid = "TODO" # UUID экземпляра услуги S3 + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + emails = "testuser1" # Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел + resource_cpu = 100 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 200 # Квота памяти пода (Указывается в Megabytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_harbor.baza.state_params["имя_ключа"] +nubes_harbor.baza.state_out["имя_ключа"] +nubes_harbor.baza.state_params_flat["имя_ключа"] +nubes_harbor.baza.state_out_flat["имя_ключа"] +nubes_harbor.baza.vault_secrets["имя_ключа"] +nubes_harbor.baza.vault_url +nubes_harbor.baza.vault_user_path +nubes_harbor.baza.vault_fields +``` diff --git a/generated/dev/docs/harbor_ops.md b/generated/dev/docs/harbor_ops.md new file mode 100644 index 0000000..9a06a19 --- /dev/null +++ b/generated/dev/docs/harbor_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](harbor_params_create.md) +- `delete` — DEV-Ветка удаления Harbor diff --git a/generated/dev/docs/harbor_outputs.md b/generated/dev/docs/harbor_outputs.md new file mode 100644 index 0000000..3422bbd --- /dev/null +++ b/generated/dev/docs/harbor_outputs.md @@ -0,0 +1,40 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.k8sregistry"]` +- `state_out_flat["monitoring.k8sregistry.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` + diff --git a/generated/dev/docs/harbor_params.md b/generated/dev/docs/harbor_params.md new file mode 100644 index 0000000..6819254 --- /dev/null +++ b/generated/dev/docs/harbor_params.md @@ -0,0 +1,12 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Parameters + +- [Create params](harbor_params_create.md) +- [Modify params](harbor_params_modify.md) diff --git a/generated/dev/docs/harbor_params_create.md b/generated/dev/docs/harbor_params_create.md new file mode 100644 index 0000000..d5f1c60 --- /dev/null +++ b/generated/dev/docs/harbor_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
110resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
231s3_uiduuidUUID экземпляра услуги S3ref_svc_id=12
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
16domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
17emailsstringtestuser1Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел
228resource_cpuinteger100Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
229resource_memoryinteger200Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
230resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
diff --git a/generated/dev/docs/harbor_params_modify.md b/generated/dev/docs/harbor_params_modify.md new file mode 100644 index 0000000..0960de7 --- /dev/null +++ b/generated/dev/docs/harbor_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/http.md b/generated/dev/docs/http.md new file mode 100644 index 0000000..68075b0 --- /dev/null +++ b/generated/dev/docs/http.md @@ -0,0 +1,13 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/http_example.md b/generated/dev/docs/http_example.md new file mode 100644 index 0000000..97655b7 --- /dev/null +++ b/generated/dev/docs/http_example.md @@ -0,0 +1,51 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Copy-ready manifest (`http_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_http" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 100 # Квота ядра пода (Указывается в mili) + resource_memory = 200 # Квота памяти пода (Указывается в Megabytes) + resource_instances = 1 # Количество узлов + registry_path = "nexus-sa.tst.nubes.ru/docker-nubes/jolt" # Путь до image, который будет установлен как deployment + json_env = {"param": "value"} # Массив переменных, будут переданы в env приложения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_http.baza.state_params["имя_ключа"] +nubes_http.baza.state_out["имя_ключа"] +nubes_http.baza.state_params_flat["имя_ключа"] +nubes_http.baza.state_out_flat["имя_ключа"] +nubes_http.baza.vault_secrets["имя_ключа"] +nubes_http.baza.vault_url +nubes_http.baza.vault_user_path +nubes_http.baza.vault_fields +``` diff --git a/generated/dev/docs/http_ops.md b/generated/dev/docs/http_ops.md new file mode 100644 index 0000000..547defd --- /dev/null +++ b/generated/dev/docs/http_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](http_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](http_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/dev/docs/http_outputs.md b/generated/dev/docs/http_outputs.md new file mode 100644 index 0000000..9baf737 --- /dev/null +++ b/generated/dev/docs/http_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/dev/docs/http_params.md b/generated/dev/docs/http_params.md new file mode 100644 index 0000000..07aa372 --- /dev/null +++ b/generated/dev/docs/http_params.md @@ -0,0 +1,12 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Parameters + +- [Create params](http_params_create.md) +- [Modify params](http_params_modify.md) diff --git a/generated/dev/docs/http_params_create.md b/generated/dev/docs/http_params_create.md new file mode 100644 index 0000000..aa7d336 --- /dev/null +++ b/generated/dev/docs/http_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
216resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
212domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
213resource_cpuinteger100Квота ядра пода (Указывается в mili)minvalue=100; maxvalue=4000
214resource_memoryinteger200Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
215resource_instancesinteger1Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robinminvalue=1; maxvalue=7
217registry_pathstringnexus-sa.tst.nubes.ru/docker-nubes/joltПуть до image, который будет установлен как deployment
241json_envjson{&quot;param&quot;: &quot;value&quot;}Массив переменных, будут переданы в env приложения
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/http_params_modify.md b/generated/dev/docs/http_params_modify.md new file mode 100644 index 0000000..75a69ec --- /dev/null +++ b/generated/dev/docs/http_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
218resource_cpuintegerminvalue=100; maxvalue=4000
219resource_memoryintegerminvalue=128; maxvalue=8192
220resource_instancesintegerКоличество узловminvalue=1; maxvalue=7
diff --git a/generated/dev/docs/index.md b/generated/dev/docs/index.md new file mode 100644 index 0000000..613a1ca --- /dev/null +++ b/generated/dev/docs/index.md @@ -0,0 +1,40 @@ +# Ресурсы провайдера + +| ID | Ресурс | Описание | +|-----|--------|----------| +| 1 | [nubes_dummy](dummy.md) | Болванка | +| 2 | [nubes_template](template.md) | Темплейт k8s | +| 12 | [nubes_s3](s3.md) | S3 Object Storage | +| 13 | [nubes_s3bucket](s3bucket.md) | S3 бакет | +| 19 | [nubes_vc_org](vc_org.md) | Организация в Cloud Director | +| 21 | [nubes_vc_vdc](vc_vdc.md) | Виртуальный датацентр (vDC) | +| 22 | [nubes_vc_nsxt](vc_nsxt.md) | Сетевой шлюз периметра (Edge) | +| 25 | [nubes_vcexternalip](vcexternalip.md) | Публичные IP адреса | +| 26 | [nubes_vapp](vapp.md) | Виртуальный каталог ВМ (vApp) | +| 28 | [nubes_vc_vm_v3](vc_vm_v3.md) | Виртуальная машина | +| 29 | [nubes_vc_vdc_group](vc_vdc_group.md) | Группа датацентров | +| 50 | [nubes_nextcloud](nextcloud.md) | Nextcloud | +| 81 | [nubes_superset](superset.md) | Apache Superset | +| 82 | [nubes_harbor](harbor.md) | Container Registry | +| 88 | [nubes_k8s_ziti_controller](k8s_ziti_controller.md) | k8sZitiController | +| 89 | [nubes_flask](flask.md) | Web-сервер с фреймворком Flask | +| 90 | [nubes_postgres](postgres.md) | PostgreSQL | +| 91 | [nubes_redis](redis.md) | Redis | +| 92 | [nubes_mongodb](mongodb.md) | MongoDB | +| 93 | [nubes_rabbitmq](rabbitmq.md) | RabbitMQ | +| 94 | [nubes_lucee](lucee.md) | Lucee | +| 95 | [nubes_nodejs](nodejs.md) | NodeJS | +| 96 | [nubes_pgadmin](pgadmin.md) | pgAdmin | +| 97 | [nubes_nodered](nodered.md) | NodeRed | +| 98 | [nubes_http](http.md) | Простой HTTP контейнер | +| 99 | [nubes_gitea](gitea.md) | Gitea | +| 109 | [nubes_zones_v2](zones_v2.md) | Управление днс-зонами и записями | +| 110 | [nubes_dnszone](dnszone.md) | DNS зона | +| 111 | [nubes_dnsrecord](dnsrecord.md) | DNS запись | +| 115 | [nubes_mariadb](mariadb.md) | Mariadb | +| 116 | [nubes_kafka](kafka.md) | ApacheKafka | +| 119 | [nubes_akhq](akhq.md) | Akhq | +| 149 | [nubes_valo_tenant](valo_tenant.md) | VALO Cloud | +| 150 | [nubes_k8s_sthutrval_cluster](k8s_sthutrval_cluster.md) | Kubernetes кластер Штурвал | +| 151 | [nubes_k8s_openbao](k8s_openbao.md) | Vault | +| 153 | [nubes_nifi](nifi.md) | Nifi | diff --git a/generated/dev/docs/k8s_openbao.md b/generated/dev/docs/k8s_openbao.md new file mode 100644 index 0000000..98c440c --- /dev/null +++ b/generated/dev/docs/k8s_openbao.md @@ -0,0 +1,13 @@ +# Resource nubes_k8s_openbao + +Service ID: `151` + +Service Name: Vault + +[Manual](k8s_openbao.md) | [Create params](k8s_openbao_params_create.md) | [Modify params](k8s_openbao_params_modify.md) | [Output params](k8s_openbao_outputs.md) | [Operations](k8s_openbao_ops.md) | [Example](k8s_openbao_example.md) + +## MAN + +
+# Инструкция по развертыванию Хранилища секретов через платформу

## 1. Общая информация
Хранилище секретов — сервис централизованного и защищённого хранения чувствительных данных (секретов, ключей, конфигураций). Предназначен для использования приложениями и сервисами в Kubernetes с управлением доступом на основе ролей.
В качестве приложения используется открытый проект OpenBao с поддержкой кластерного режима RAFT.

### Доступные операции
**create** — Создание хранилища секретов и инициализация кластера.
**delete** — Удаление хранилища секретов с полной очисткой всех связанных сущностей.
**modify** — Изменение параметров развернутого хранилища секретов.
**suspend** — Приостановка работы кластера без удаления Persistent Volume.
**resume** — Возобновление работы кластера с автоматическим unseal всех нод.
**restart** — Поочередный перезапуск нод с последующим подключением в кластер.
**create_kv** — Создание Key/Value хранилища версии V2.
**delete_kv** — Удаление Key/Value хранилища со всеми содержащимися секретами.
**create_secrets** — Создание секрета внутри Key/Value хранилища.
**delete_secrets** — Удаление секрета из Key/Value хранилища.
**update_secrets** — Обновление значения секрета.
**recovery_secrets** — Откат секрета к указанной версии.
**create_role** — Создание роли для управления Key/Value хранилищем или секретами.
**update_role** — Обновление параметров роли.
**delete_role** — Удаление роли доступа.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: Кластер Kubernetes, в котором будет развернуто приложение.
Формат значения: имя Kubernetes-кластера.

### Имя домена
Описание: Доменное имя сервиса. При указании полного FQDN выполняется проверка DNS A-записи или добавляется А-запись в техническую зону.
Формат значения: secrets.domain.com / mysecretdomain.

### Кол-во реплик
Описание: Количество узлов в кластере.
Формат значения: целое число.

### Квота ядра пода
Описание: Лимит CPU для одного узла. Указывается в milicores (1000 milicores = 1 CPU).
Формат значения: целое число.

### Квота памяти пода
Описание: Лимит оперативной памяти для одного узла. Указывается в megabytes.
Формат значения: целое число.

### Размер диска под хранение секретов
Описание: Объем диска для хранения данных. Фактический объем равен значению, умноженному на количество реплик. Указывается в Gigabytes
Формат значения: целое число.

### Версия OpenBao
Описание: Версия OpenBao для развертывания сервиса.
Формат значения: выбор из предоставленного списка версий.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Кол-во реплик: 2.
Квота ядра пода: 200.
Квота памяти пода: 256.
Размер диска под хранение секретов: 1.
Версия OpenBao: актуальная стабильная версия.

### Промышленное окружение
Кол-во реплик: от 3.
Квота ядра пода: от 1000.
Квота памяти пода: от 1024.
Размер диска под хранение секретов: от 10.
Версия OpenBao: актуальная стабильная версия.

---

## 4. Выходные параметры
Настройки внутри хранилища (kv/secrets/roles) — конфигурации Key/Value, секретов и ролей, созданные соответствующими операциями.
Настройки самого хранилище (сайзинг, объемы) — параметры ресурсов и хранилища данных, примененные к сервису.
Статус кластера (running / suspended) — текущее состояние работы кластера.
url для подключения к веб — адрес веб-интерфейса сервиса.

---

## 5. Дополнительная информация
При удалении хранилища все данные и настройки безвозвратно удаляются.
При использовании доменного имени требуется корректная настройка DNS.
Операции управления секретами и ролями выполняются только после успешного создания хранилища.
Удаление кластера производится через операцию `suspend`, а удаление становится доступно через 14 дней после остановки кластера
Правило выше не относится к операции `delete_kv` / `delete_secrets` / `delete_role` +
diff --git a/generated/dev/docs/k8s_openbao_example.md b/generated/dev/docs/k8s_openbao_example.md new file mode 100644 index 0000000..e62aa3a --- /dev/null +++ b/generated/dev/docs/k8s_openbao_example.md @@ -0,0 +1,51 @@ +# Resource nubes_k8s_openbao + +Service ID: `151` + +Service Name: Vault + +[Manual](k8s_openbao.md) | [Create params](k8s_openbao_params_create.md) | [Modify params](k8s_openbao_params_modify.md) | [Output params](k8s_openbao_outputs.md) | [Operations](k8s_openbao_ops.md) | [Example](k8s_openbao_example.md) + +## Copy-ready manifest (`k8s_openbao_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_k8s_openbao" "baza" { + resource_realm = "TODO" # Кластер Kubernetes в котором будет развёрнуто приложение + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Пример: `500` + resource_instances = 3 # Пример: `3` + resource_memory = 512 # Пример: `2048` + resource_disk = 10 # Пример: `10` + app_version = 2.4.4 # Выбрать из списка + domain = "bao-" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_k8s_openbao.baza.state_params["имя_ключа"] +nubes_k8s_openbao.baza.state_out["имя_ключа"] +nubes_k8s_openbao.baza.state_params_flat["имя_ключа"] +nubes_k8s_openbao.baza.state_out_flat["имя_ключа"] +nubes_k8s_openbao.baza.vault_secrets["имя_ключа"] +nubes_k8s_openbao.baza.vault_url +nubes_k8s_openbao.baza.vault_user_path +nubes_k8s_openbao.baza.vault_fields +``` diff --git a/generated/dev/docs/k8s_openbao_ops.md b/generated/dev/docs/k8s_openbao_ops.md new file mode 100644 index 0000000..e59e9c1 --- /dev/null +++ b/generated/dev/docs/k8s_openbao_ops.md @@ -0,0 +1,26 @@ +# Resource nubes_k8s_openbao + +Service ID: `151` + +Service Name: Vault + +[Manual](k8s_openbao.md) | [Create params](k8s_openbao_params_create.md) | [Modify params](k8s_openbao_params_modify.md) | [Output params](k8s_openbao_outputs.md) | [Operations](k8s_openbao_ops.md) | [Example](k8s_openbao_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](k8s_openbao_params_create.md) +- `create_kv` — Создание кластера. Параметры: [Create params](k8s_openbao_params_create.md) +- `create_policy` — Создание кластера. Параметры: [Create params](k8s_openbao_params_create.md) +- `create_secrets` — Создание кластера. Параметры: [Create params](k8s_openbao_params_create.md) +- `delete` +- `delete_kv` — **ОСТОРОЖНО** Удаление KV подразумевает удаление ВСЕХ secrets внутри этой KV +- `delete_policy` +- `delete_secrets` +- `modify` — Изменение кластера. Параметры: [Modify params](k8s_openbao_params_modify.md) +- `reconcile` +- `recovery_secrets` — Данная операция подразумевает откат на конкретную версию в секрете. **Это не восстановление удалённого секрета** +- `restart` +- `resume` +- `suspend` +- `update_policy` +- `update_secrets` diff --git a/generated/dev/docs/k8s_openbao_outputs.md b/generated/dev/docs/k8s_openbao_outputs.md new file mode 100644 index 0000000..5d750bd --- /dev/null +++ b/generated/dev/docs/k8s_openbao_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_k8s_openbao + +Service ID: `151` + +Service Name: Vault + +[Manual](k8s_openbao.md) | [Create params](k8s_openbao_params_create.md) | [Modify params](k8s_openbao_params_modify.md) | [Output params](k8s_openbao_outputs.md) | [Operations](k8s_openbao_ops.md) | [Example](k8s_openbao_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/k8s_openbao_params.md b/generated/dev/docs/k8s_openbao_params.md new file mode 100644 index 0000000..f87adc9 --- /dev/null +++ b/generated/dev/docs/k8s_openbao_params.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_openbao + +Service ID: `151` + +Service Name: Vault + +[Manual](k8s_openbao.md) | [Create params](k8s_openbao_params_create.md) | [Modify params](k8s_openbao_params_modify.md) | [Output params](k8s_openbao_outputs.md) | [Operations](k8s_openbao_ops.md) | [Example](k8s_openbao_example.md) + +## Parameters + +- [Create params](k8s_openbao_params_create.md) +- [Modify params](k8s_openbao_params_modify.md) diff --git a/generated/dev/docs/k8s_openbao_params_create.md b/generated/dev/docs/k8s_openbao_params_create.md new file mode 100644 index 0000000..984f213 --- /dev/null +++ b/generated/dev/docs/k8s_openbao_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_k8s_openbao + +Service ID: `151` + +Service Name: Vault + +[Manual](k8s_openbao.md) | [Create params](k8s_openbao_params_create.md) | [Modify params](k8s_openbao_params_modify.md) | [Output params](k8s_openbao_outputs.md) | [Operations](k8s_openbao_ops.md) | [Example](k8s_openbao_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
663resource_realmstringВыбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы
func=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
664resource_cpuinteger500Указывается в Milicores
`1000` Mili == `1` Ядро
minvalue=250; maxvalue=8000
665resource_instancesinteger3Пример: `3`minvalue=1; maxvalue=7
666resource_memoryinteger512Указывается в Megabytesminvalue=256; maxvalue=8192
667resource_diskinteger10Указывается в Gigabytesminvalue=1; maxvalue=999
668app_versionstring2.4.4Выбрать из спискаvalue_list=2.4.4
669domainstringbao-Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/k8s_openbao_params_modify.md b/generated/dev/docs/k8s_openbao_params_modify.md new file mode 100644 index 0000000..cf43f39 --- /dev/null +++ b/generated/dev/docs/k8s_openbao_params_modify.md @@ -0,0 +1,19 @@ +# Resource nubes_k8s_openbao + +Service ID: `151` + +Service Name: Vault + +[Manual](k8s_openbao.md) | [Create params](k8s_openbao_params_create.md) | [Modify params](k8s_openbao_params_modify.md) | [Output params](k8s_openbao_outputs.md) | [Operations](k8s_openbao_ops.md) | [Example](k8s_openbao_example.md) + +## Modify params + + + + + + + + + +
IDCodeTypeDescriptionConstraints
670resource_instancesintegerПример: `3`minvalue=1; maxvalue=7
671resource_cpuintegerУказывается в Milicores
`1000` Mili == `1` Ядро
minvalue=250; maxvalue=8000
672resource_memoryintegerУказывается в Megabytesminvalue=256; maxvalue=8192
673resource_diskintegerУказывается в Gigabytesminvalue=1; maxvalue=999
674app_versionstringВыбрать из спискаvalue_list=2.4.4
diff --git a/generated/dev/docs/k8s_sthutrval_cluster.md b/generated/dev/docs/k8s_sthutrval_cluster.md new file mode 100644 index 0000000..b3b0523 --- /dev/null +++ b/generated/dev/docs/k8s_sthutrval_cluster.md @@ -0,0 +1,13 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## MAN + +
+Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле +
diff --git a/generated/dev/docs/k8s_sthutrval_cluster_example.md b/generated/dev/docs/k8s_sthutrval_cluster_example.md new file mode 100644 index 0000000..fb256b8 --- /dev/null +++ b/generated/dev/docs/k8s_sthutrval_cluster_example.md @@ -0,0 +1,49 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Copy-ready manifest (`k8s_sthutrval_cluster_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_k8s_sthutrval_cluster" "baza" { + startup_configuration = "TODO" + control_plane_configuration = "TODO" + access_configuration = "TODO" + cluster_configuration = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + worker_configuration = [{}] +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_k8s_sthutrval_cluster.baza.state_params["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_out["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_params_flat["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_out_flat["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.vault_secrets["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.vault_url +nubes_k8s_sthutrval_cluster.baza.vault_user_path +nubes_k8s_sthutrval_cluster.baza.vault_fields +``` diff --git a/generated/dev/docs/k8s_sthutrval_cluster_ops.md b/generated/dev/docs/k8s_sthutrval_cluster_ops.md new file mode 100644 index 0000000..afb94fd --- /dev/null +++ b/generated/dev/docs/k8s_sthutrval_cluster_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](k8s_sthutrval_cluster_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](k8s_sthutrval_cluster_params_modify.md) +- `reconcile` +- `resume` +- `suspend` +- `update_secrets` diff --git a/generated/dev/docs/k8s_sthutrval_cluster_outputs.md b/generated/dev/docs/k8s_sthutrval_cluster_outputs.md new file mode 100644 index 0000000..065be3b --- /dev/null +++ b/generated/dev/docs/k8s_sthutrval_cluster_outputs.md @@ -0,0 +1,33 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["ingressAddress"]` +- `state_out_flat["kubernetesApiAddress"]` + +### `vault_secrets` ключи + +- `vault_secrets["kubernetesAdminConfigB64"]` + diff --git a/generated/dev/docs/k8s_sthutrval_cluster_params.md b/generated/dev/docs/k8s_sthutrval_cluster_params.md new file mode 100644 index 0000000..641f838 --- /dev/null +++ b/generated/dev/docs/k8s_sthutrval_cluster_params.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Parameters + +- [Create params](k8s_sthutrval_cluster_params_create.md) +- [Modify params](k8s_sthutrval_cluster_params_modify.md) diff --git a/generated/dev/docs/k8s_sthutrval_cluster_params_create.md b/generated/dev/docs/k8s_sthutrval_cluster_params_create.md new file mode 100644 index 0000000..4ecc132 --- /dev/null +++ b/generated/dev/docs/k8s_sthutrval_cluster_params_create.md @@ -0,0 +1,39 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
750startup_configurationmap-fixed
751control_plane_configurationmap-fixed
753access_configurationmap-fixed
755cluster_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
752worker_configurationarray-map-fixed[{}]
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/k8s_sthutrval_cluster_params_modify.md b/generated/dev/docs/k8s_sthutrval_cluster_params_modify.md new file mode 100644 index 0000000..f2f26fb --- /dev/null +++ b/generated/dev/docs/k8s_sthutrval_cluster_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
756cluster_configurationmap-fixed
757control_plane_configurationmap-fixed
758worker_configurationarray-map-fixed
759access_configurationmap-fixed
diff --git a/generated/dev/docs/k8s_ziti_controller.md b/generated/dev/docs/k8s_ziti_controller.md new file mode 100644 index 0000000..5c8b8f3 --- /dev/null +++ b/generated/dev/docs/k8s_ziti_controller.md @@ -0,0 +1,13 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/k8s_ziti_controller_example.md b/generated/dev/docs/k8s_ziti_controller_example.md new file mode 100644 index 0000000..bb586b6 --- /dev/null +++ b/generated/dev/docs/k8s_ziti_controller_example.md @@ -0,0 +1,50 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Copy-ready manifest (`k8s_ziti_controller_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_k8s_ziti_controller" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 300 # Квота ядра пода (milicores) + resource_memory = 512 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_k8s_ziti_controller.baza.state_params["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_out["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_params_flat["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_out_flat["имя_ключа"] +nubes_k8s_ziti_controller.baza.vault_secrets["имя_ключа"] +nubes_k8s_ziti_controller.baza.vault_url +nubes_k8s_ziti_controller.baza.vault_user_path +nubes_k8s_ziti_controller.baza.vault_fields +``` diff --git a/generated/dev/docs/k8s_ziti_controller_ops.md b/generated/dev/docs/k8s_ziti_controller_ops.md new file mode 100644 index 0000000..8da7234 --- /dev/null +++ b/generated/dev/docs/k8s_ziti_controller_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](k8s_ziti_controller_params_create.md) +- `delete` diff --git a/generated/dev/docs/k8s_ziti_controller_outputs.md b/generated/dev/docs/k8s_ziti_controller_outputs.md new file mode 100644 index 0000000..6fc1fdd --- /dev/null +++ b/generated/dev/docs/k8s_ziti_controller_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/k8s_ziti_controller_params.md b/generated/dev/docs/k8s_ziti_controller_params.md new file mode 100644 index 0000000..343d93e --- /dev/null +++ b/generated/dev/docs/k8s_ziti_controller_params.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Parameters + +- [Create params](k8s_ziti_controller_params_create.md) +- [Modify params](k8s_ziti_controller_params_modify.md) diff --git a/generated/dev/docs/k8s_ziti_controller_params_create.md b/generated/dev/docs/k8s_ziti_controller_params_create.md new file mode 100644 index 0000000..dac54da --- /dev/null +++ b/generated/dev/docs/k8s_ziti_controller_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
298resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
294resource_cpuinteger300Квота ядра пода (milicores)minvalue=100; maxvalue=4000
295resource_memoryinteger512Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
296resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
297resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
299domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
diff --git a/generated/dev/docs/k8s_ziti_controller_params_modify.md b/generated/dev/docs/k8s_ziti_controller_params_modify.md new file mode 100644 index 0000000..1998e6a --- /dev/null +++ b/generated/dev/docs/k8s_ziti_controller_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/kafka.md b/generated/dev/docs/kafka.md new file mode 100644 index 0000000..34f2d04 --- /dev/null +++ b/generated/dev/docs/kafka.md @@ -0,0 +1,13 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## MAN + +
+# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Идентификатор организации
Уникальный идентификатор созданной организации. Используется для всех дальнейших API-операций.

### Статус организации
Отражает текущее состояние: active, suspended, modifying, deleting.

### Параметры сети
Выходные данные при наличии ipSpaces: список выделенных внешних IP, имя ip_space, количество IP.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)**;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к этой организации.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.

+
diff --git a/generated/dev/docs/kafka_example.md b/generated/dev/docs/kafka_example.md new file mode 100644 index 0000000..77ed3dc --- /dev/null +++ b/generated/dev/docs/kafka_example.md @@ -0,0 +1,50 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Copy-ready manifest (`kafka_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_kafka" "baza" { + resource_instances = 1 # Пример: `2` + resource_memory = 1 # Пример: `2048` + resource_cpu = 1 # Пример: `2000` + resource_disk = 1 # Пример: `20` + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_kafka.baza.state_params["имя_ключа"] +nubes_kafka.baza.state_out["имя_ключа"] +nubes_kafka.baza.state_params_flat["имя_ключа"] +nubes_kafka.baza.state_out_flat["имя_ключа"] +nubes_kafka.baza.vault_secrets["имя_ключа"] +nubes_kafka.baza.vault_url +nubes_kafka.baza.vault_user_path +nubes_kafka.baza.vault_fields +``` diff --git a/generated/dev/docs/kafka_ops.md b/generated/dev/docs/kafka_ops.md new file mode 100644 index 0000000..21e6e30 --- /dev/null +++ b/generated/dev/docs/kafka_ops.md @@ -0,0 +1,21 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](kafka_params_create.md) +- `create_topic` — Создание кластера. Параметры: [Create params](kafka_params_create.md) +- `create_user` — Создание кластера. Параметры: [Create params](kafka_params_create.md) +- `delete` +- `delete_topic` +- `delete_user` — # Инструкция по управлению пользователями Apache Kafka. --- ## 1. Общая информация Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру. Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам. Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka. --- ## Доступные операции - **create_user** — Создание пользователя - **modify_user** — Модификация пользователя - **delete_user** — Удаление пользователя --- ## 2. Параметры создания пользователя Ниже перечислены параметры, которые необходимо указать при создании нового пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 3. Параметры модификации пользователя Ниже перечислены параметры, которые необходимо указать при модификации пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 4. Параметры удалении пользователя Ниже перечислены параметры, которые необходимо указать при удалении пользователя: * **Имя пользователя** Имя удаляемого пользователя. Пример: `username-admin` ## 5. Выходные параметры После успешного создания пользователя платформа возвращает параметры, необходимые для подключения: * **ca.crt** Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении. Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **ca.p12** PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.p12 ``` * **ca.password** Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно. * **user.crt** Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka. Используется совместно с приватным ключом `user.key` для аутентификации. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **user.key** Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.key ``` * **user.p12** PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo "<содержание поля>" | base64 -d > <название>.p12 ``` * **user.password** Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно. Эти параметры используются для настройки TLS-подключения. --- ## 6. Пример конфигурации клиента внутри кластера Пример пода: ```yaml apiVersion: v1 kind: Pod metadata: name: kafka-4 spec: volumes: - name: work emptyDir: {} containers: - name: kcat image: quay.io/strimzi/kafka:latest-kafka-4.0.0 command: ["sleep", "5600"] volumeMounts: - name: work mountPath: /work restartPolicy: Never ``` Создаём под и сохраняем сертификаты: ```bash kubectl cp user.p12 kafka-4:/work/ kubectl cp ca.p12 kafka-4:/work/ ``` Создаём файлы конфигурации: ```bash cat > /work/user-read.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` ```bash cat > /work/user-write.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user-write.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` Листинг доступных топиков: ```bash /opt/kafka/bin/kafka-topics.sh \ --bootstrap-server kafkak8s-bootstrap-external:9093 \ --command-config /work/user-read.properties \ --list ``` Запись сообщений: ```bash /opt/kafka/bin/kafka-console-producer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --producer.config /work/user-write.properties ``` Чтение сообщений: ```bash /opt/kafka/bin/kafka-console-consumer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --from-beginning \ --group \ --consumer.config /work/user-read.properties \ --max-messages 50 ``` --- ## 7. Пример внешнего подключения с использованием **kcat** **Листинг топиков** ```bash kcat -b :9093 \ -L \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` **Запись сообщения** ```bash echo "hello from kcat" | kcat -P \ -b :9093 \ -t write-test \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-write.crt \ -X ssl.key.location=./user-write.key \ -X ssl.endpoint.identification.algorithm=none ``` **Чтение сообщений** ```bash kcat -C \ -b :9093 \ -t write-test \ -o beginning \ -q \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` --- ## 9. Рекомендации * Минимизируйте набор разрешённых операций для повышения безопасности. * Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов. * Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**. * После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам. --- +- `modify` — Изменение кластера. Параметры: [Modify params](kafka_params_modify.md) +- `modify_topic` — Изменение кластера. Параметры: [Modify params](kafka_params_modify.md) +- `modify_user` — Изменение кластера. Параметры: [Modify params](kafka_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/kafka_outputs.md b/generated/dev/docs/kafka_outputs.md new file mode 100644 index 0000000..7a36056 --- /dev/null +++ b/generated/dev/docs/kafka_outputs.md @@ -0,0 +1,54 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.bootstrap"]` +- `state_out_flat["externalConnect.bootstrap.fqdn"]` +- `state_out_flat["externalConnect.bootstrap.ip"]` +- `state_out_flat["externalConnect.bootstrap.isExternal"]` +- `state_out_flat["externalConnect.bootstrap.uuid"]` +- `state_out_flat["internalConnect"]` +- `state_out_flat["kafkaTopics"]` +- `state_out_flat["kafkaTopics.test-events"]` +- `state_out_flat["kafkaTopics.test-events.partitions"]` +- `state_out_flat["kafkaTopics.test-events.replicas"]` +- `state_out_flat["kafkaUsers"]` +- `state_out_flat["kafkaUsers.usertest"]` +- `state_out_flat["kafkaUsers.usertest.accessHosts"]` +- `state_out_flat["kafkaUsers.usertest.group"]` +- `state_out_flat["kafkaUsers.usertest.nameTopic"]` +- `state_out_flat["kafkaUsers.usertest.operations"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["users"]` + diff --git a/generated/dev/docs/kafka_params.md b/generated/dev/docs/kafka_params.md new file mode 100644 index 0000000..25cb573 --- /dev/null +++ b/generated/dev/docs/kafka_params.md @@ -0,0 +1,12 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Parameters + +- [Create params](kafka_params_create.md) +- [Modify params](kafka_params_modify.md) diff --git a/generated/dev/docs/kafka_params_create.md b/generated/dev/docs/kafka_params_create.md new file mode 100644 index 0000000..1a43a4e --- /dev/null +++ b/generated/dev/docs/kafka_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
461resource_instancesintegerПример: `2`minvalue=1; value_list=1, 2, 3, 4, 5
462resource_memoryintegerПример: `2048`minvalue=1000
463resource_cpuintegerПример: `2000`minvalue=1000
464resource_diskintegerПример: `20`
466ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
469resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
465need_external_address_masterbooleanfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`value_list=false, true
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/kafka_params_modify.md b/generated/dev/docs/kafka_params_modify.md new file mode 100644 index 0000000..e6ff431 --- /dev/null +++ b/generated/dev/docs/kafka_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
501resource_instancesintegerПример: `2`value_list=1, 2, 3, 4, 5
502resource_memoryintegerПример: `2048`minvalue=1000
503resource_cpuПример: `2000`minvalue=1000
504resource_diskintegerПример: `20`
505need_external_address_masterbooleanФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`value_list=false, true
506ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
diff --git a/generated/dev/docs/lucee.md b/generated/dev/docs/lucee.md new file mode 100644 index 0000000..ca3542d --- /dev/null +++ b/generated/dev/docs/lucee.md @@ -0,0 +1,13 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/lucee_example.md b/generated/dev/docs/lucee_example.md new file mode 100644 index 0000000..82b4927 --- /dev/null +++ b/generated/dev/docs/lucee_example.md @@ -0,0 +1,52 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Copy-ready manifest (`lucee_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_lucee" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "lucee-" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + git_path = "https://github.com/xahys/testlucee" # Путь до Git, который будет webroot для сайта + json_env = { "param": "value" } # Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения. + resource_cpu = 300 # Квота ядра пода (milicores) + resource_memory = 512 # Квота памяти пода (MBytes) + resource_instances = 1 # Количество узлов + app_version = 5.4 # Версия Lucee +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_lucee.baza.state_params["имя_ключа"] +nubes_lucee.baza.state_out["имя_ключа"] +nubes_lucee.baza.state_params_flat["имя_ключа"] +nubes_lucee.baza.state_out_flat["имя_ключа"] +nubes_lucee.baza.vault_secrets["имя_ключа"] +nubes_lucee.baza.vault_url +nubes_lucee.baza.vault_user_path +nubes_lucee.baza.vault_fields +``` diff --git a/generated/dev/docs/lucee_ops.md b/generated/dev/docs/lucee_ops.md new file mode 100644 index 0000000..3294ee8 --- /dev/null +++ b/generated/dev/docs/lucee_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](lucee_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](lucee_params_modify.md) +- `redeploy` +- `restart` +- `resume` +- `suspend` diff --git a/generated/dev/docs/lucee_outputs.md b/generated/dev/docs/lucee_outputs.md new file mode 100644 index 0000000..838f21d --- /dev/null +++ b/generated/dev/docs/lucee_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/dev/docs/lucee_params.md b/generated/dev/docs/lucee_params.md new file mode 100644 index 0000000..730e3b2 --- /dev/null +++ b/generated/dev/docs/lucee_params.md @@ -0,0 +1,12 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Parameters + +- [Create params](lucee_params_create.md) +- [Modify params](lucee_params_modify.md) diff --git a/generated/dev/docs/lucee_params_create.md b/generated/dev/docs/lucee_params_create.md new file mode 100644 index 0000000..84476c6 --- /dev/null +++ b/generated/dev/docs/lucee_params_create.md @@ -0,0 +1,43 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
108resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
132health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
103domainstringlucee-Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
104git_pathstringhttps://github.com/xahys/testluceeПуть до Git, который будет webroot для сайта
105json_envjson{ "param": "value" }Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения.maxlength=2048
106resource_cpuinteger300Квота ядра пода (milicores)minvalue=100; maxvalue=4000
107resource_memoryinteger512Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
139resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
263app_versionstring5.4Версия Luceeregex=^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$; value_list=5.4, 6.0
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/lucee_params_modify.md b/generated/dev/docs/lucee_params_modify.md new file mode 100644 index 0000000..087ab68 --- /dev/null +++ b/generated/dev/docs/lucee_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
133git_pathstringПуть до Git, который будет webroot для сайта
134health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код.
135json_envjsonМассив переменных, будут переданы в env приложенияmaxlength=2048
136resource_cpuintegerКвота ядра пода (Указывается в mili)minvalue=100; maxvalue=4000
137resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
138resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
264app_versionstringregex=^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$; value_list=5.4, 6.0
diff --git a/generated/dev/docs/mariadb.md b/generated/dev/docs/mariadb.md new file mode 100644 index 0000000..9161094 --- /dev/null +++ b/generated/dev/docs/mariadb.md @@ -0,0 +1,13 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## MAN + +
+Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление. +
diff --git a/generated/dev/docs/mariadb_example.md b/generated/dev/docs/mariadb_example.md new file mode 100644 index 0000000..a684bab --- /dev/null +++ b/generated/dev/docs/mariadb_example.md @@ -0,0 +1,57 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Copy-ready manifest (`mariadb_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_mariadb" "baza" { + resource_realm = "TODO" # Платформа для развертывания + s3_uid = "TODO" # Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (milicores) + resource_memory = 1024 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + need_external_address_master = false # Выделяет внешний (белый) IP для доступа к master + ext_backup_schedule = "0 * * * *" # Не обязательный. Время запуска бекапа + app_version = 9.4.0 # Версия MySQL + auto_scale = false # Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + auto_scale_percentage = 10 # Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + auto_scale_tech_window = 0 # Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + auto_scale_quota_gb = 1 # Квота для autoScale +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_mariadb.baza.state_params["имя_ключа"] +nubes_mariadb.baza.state_out["имя_ключа"] +nubes_mariadb.baza.state_params_flat["имя_ключа"] +nubes_mariadb.baza.state_out_flat["имя_ключа"] +nubes_mariadb.baza.vault_secrets["имя_ключа"] +nubes_mariadb.baza.vault_url +nubes_mariadb.baza.vault_user_path +nubes_mariadb.baza.vault_fields +``` diff --git a/generated/dev/docs/mariadb_ops.md b/generated/dev/docs/mariadb_ops.md new file mode 100644 index 0000000..14fa2f8 --- /dev/null +++ b/generated/dev/docs/mariadb_ops.md @@ -0,0 +1,19 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](mariadb_params_create.md) +- `create_database` — Создание кластера. Параметры: [Create params](mariadb_params_create.md) +- `create_user` — Создание кластера. Параметры: [Create params](mariadb_params_create.md) +- `delete` +- `delete_database` +- `delete_user` +- `modify` — Изменение кластера. Параметры: [Modify params](mariadb_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/mariadb_outputs.md b/generated/dev/docs/mariadb_outputs.md new file mode 100644 index 0000000..38f0606 --- /dev/null +++ b/generated/dev/docs/mariadb_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/mariadb_params.md b/generated/dev/docs/mariadb_params.md new file mode 100644 index 0000000..1d0477b --- /dev/null +++ b/generated/dev/docs/mariadb_params.md @@ -0,0 +1,12 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Parameters + +- [Create params](mariadb_params_create.md) +- [Modify params](mariadb_params_modify.md) diff --git a/generated/dev/docs/mariadb_params_create.md b/generated/dev/docs/mariadb_params_create.md new file mode 100644 index 0000000..4f6f74c --- /dev/null +++ b/generated/dev/docs/mariadb_params_create.md @@ -0,0 +1,48 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
422resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
429ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster
459s3_uiduuidЭкземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательноref_svc_id=12
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
424resource_cpuinteger500Квота ядра пода (milicores)minvalue=500; maxvalue=4000
425resource_memoryinteger1024Квота памяти пода (MBytes)minvalue=1024; maxvalue=8192
426resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
427resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
428need_external_address_masterbooleanfalseВыделяет внешний (белый) IP для доступа к mastervalue_list=false, true
430ext_backup_schedulestring0 * * * *Не обязательный. Время запуска бекапаregex=^(\*|(\d+|\*\/\d+|\d+(,\d+)*))\s\*\s\*\s\*\s\*$
431app_versionstring9.4.0Версия MySQLvalue_list=8.4.0, 9.4.0
432auto_scalebooleanfalseВключает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услугиvalue_list=false, true
433auto_scale_percentageinteger10Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объемаvalue_list=10, 15, 20
434auto_scale_tech_windowinteger0Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)value_list=0, 5, 12, 23
435auto_scale_quota_gbinteger1Квота для autoScale
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/mariadb_params_modify.md b/generated/dev/docs/mariadb_params_modify.md new file mode 100644 index 0000000..c17f0d8 --- /dev/null +++ b/generated/dev/docs/mariadb_params_modify.md @@ -0,0 +1,25 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Modify params + + + + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
436resource_cpuinteger
437resource_memoryinteger
438resource_instancesinteger
439resource_diskinteger
440need_external_address_masterbooleanvalue_list=false, true
441ip_space_name_master
442ext_backup_schedulestring
443auto_scalebooleanvalue_list=false, true
444auto_scale_percentageinteger
445auto_scale_tech_windowinteger
446auto_scale_quota_gbinteger
diff --git a/generated/dev/docs/mongodb.md b/generated/dev/docs/mongodb.md new file mode 100644 index 0000000..8f1fa29 --- /dev/null +++ b/generated/dev/docs/mongodb.md @@ -0,0 +1,13 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## MAN + +
+# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание базы
* **delete** — удаление базы данных
* **suspend** — остановка базы данных
* **resume** — запуск базы данных
* **modify** — изменение базы данных

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания базы данных

Эти параметры необходимо указать при создании базы данных.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Оптимально выбирать нечётное количество узлов в кластере MongoDB, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Выделение внешнего IP для доступа к master

Флаг, определяющий необходимость выделения внешнего IP.
Допустимые значения: `true` или `false`

### Имя ipSpace для публикации внешнего IP

Указывается только если включено выделение внешнего IP.
Пример: `internet-no-antiddos-v1`

---

## 3. Параметры модификации базы данных

Все параметры, которые вы указываете при модификации, являются необязательными — система автоматически подставит ранее сохранённые значения, и при необходимости вы можете изменить их на нужные.

### Количество узлов

Оптимально выбирать нечётное количество узлов в кластере MongoDB, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных указывается в гигабайтах. Его можно увеличить при необходимости, однако уменьшить уже выделенный размер невозможно.
Пример: `20`

### Выделение внешнего IP для доступа к master

Флаг, определяющий необходимость выделения внешнего IP.
Допустимые значения: `true` или `false`

### Имя ipSpace для публикации внешнего IP

Указывается только если включено выделение внешнего IP.
Пример: `internet-no-antiddos-v1`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания базы данных платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю. +
diff --git a/generated/dev/docs/mongodb_example.md b/generated/dev/docs/mongodb_example.md new file mode 100644 index 0000000..c263900 --- /dev/null +++ b/generated/dev/docs/mongodb_example.md @@ -0,0 +1,50 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Copy-ready manifest (`mongodb_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_mongodb" "baza" { + resource_instances = 1 # Пример: `2` + resource_memory = 1 # Пример: `2048` + resource_cpu = 1 # Пример: `1000` + resource_disk = 1 # Пример: `20` + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_mongodb.baza.state_params["имя_ключа"] +nubes_mongodb.baza.state_out["имя_ключа"] +nubes_mongodb.baza.state_params_flat["имя_ключа"] +nubes_mongodb.baza.state_out_flat["имя_ключа"] +nubes_mongodb.baza.vault_secrets["имя_ключа"] +nubes_mongodb.baza.vault_url +nubes_mongodb.baza.vault_user_path +nubes_mongodb.baza.vault_fields +``` diff --git a/generated/dev/docs/mongodb_ops.md b/generated/dev/docs/mongodb_ops.md new file mode 100644 index 0000000..1e923ef --- /dev/null +++ b/generated/dev/docs/mongodb_ops.md @@ -0,0 +1,18 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](mongodb_params_create.md) +- `create_user` — Создание кластера. Параметры: [Create params](mongodb_params_create.md) +- `delete` +- `delete_user` +- `modify` — Изменение кластера. Параметры: [Modify params](mongodb_params_modify.md) +- `modify_user` — Изменение кластера. Параметры: [Modify params](mongodb_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/mongodb_outputs.md b/generated/dev/docs/mongodb_outputs.md new file mode 100644 index 0000000..2a6b2dc --- /dev/null +++ b/generated/dev/docs/mongodb_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["internalConnect"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/dev/docs/mongodb_params.md b/generated/dev/docs/mongodb_params.md new file mode 100644 index 0000000..b595ec1 --- /dev/null +++ b/generated/dev/docs/mongodb_params.md @@ -0,0 +1,12 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Parameters + +- [Create params](mongodb_params_create.md) +- [Modify params](mongodb_params_modify.md) diff --git a/generated/dev/docs/mongodb_params_create.md b/generated/dev/docs/mongodb_params_create.md new file mode 100644 index 0000000..650eb5e --- /dev/null +++ b/generated/dev/docs/mongodb_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
487resource_instancesintegerПример: `2`minvalue=1; maxvalue=7
588resource_memoryintegerПример: `2048`minvalue=500
589resource_cpuintegerПример: `1000`
590resource_diskintegerПример: `20`
595resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
646ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
645need_external_address_masterbooleanfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/mongodb_params_modify.md b/generated/dev/docs/mongodb_params_modify.md new file mode 100644 index 0000000..e00c6cc --- /dev/null +++ b/generated/dev/docs/mongodb_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
648resource_instancesintegerПример: `2`
649resource_memoryintegerПример: `2048`
650resource_cpuintegerПример: `1000`
651resource_diskintegerПример: `20`
652need_external_address_masterbooleanФлаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`
653ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
diff --git a/generated/dev/docs/nextcloud.md b/generated/dev/docs/nextcloud.md new file mode 100644 index 0000000..a494623 --- /dev/null +++ b/generated/dev/docs/nextcloud.md @@ -0,0 +1,13 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## MAN + +
+Ресурсная платформа: k8s. Автоматизация: helm chart +
diff --git a/generated/dev/docs/nextcloud_example.md b/generated/dev/docs/nextcloud_example.md new file mode 100644 index 0000000..c05ddc4 --- /dev/null +++ b/generated/dev/docs/nextcloud_example.md @@ -0,0 +1,53 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Copy-ready manifest (`nextcloud_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nextcloud" "baza" { + psql_uid = "TODO" + redis_uid = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 1000 # Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili + resource_memory = 1024 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 2 # Квота диска (Указывается в GIGAbytes) Диск не может быть меньше 2G (!) + document_server = "collabora" # Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен Размер пода collabora: 1.8 ядра. 2Gi ОЗУ + resource_instances = 1 # Количество узлов Не может пока быть больше одного, так как нужны SC под RWMany + app_version = 32.0.1 +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nextcloud.baza.state_params["имя_ключа"] +nubes_nextcloud.baza.state_out["имя_ключа"] +nubes_nextcloud.baza.state_params_flat["имя_ключа"] +nubes_nextcloud.baza.state_out_flat["имя_ключа"] +nubes_nextcloud.baza.vault_secrets["имя_ключа"] +nubes_nextcloud.baza.vault_url +nubes_nextcloud.baza.vault_user_path +nubes_nextcloud.baza.vault_fields +``` diff --git a/generated/dev/docs/nextcloud_ops.md b/generated/dev/docs/nextcloud_ops.md new file mode 100644 index 0000000..2dd5b5f --- /dev/null +++ b/generated/dev/docs/nextcloud_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nextcloud_params_create.md) +- `delete` — DEV-Ветка удаления Nextcloud +- `modify` — Изменение кластера. Параметры: [Modify params](nextcloud_params_modify.md) +- `restart` +- `resume` +- `suspend` diff --git a/generated/dev/docs/nextcloud_outputs.md b/generated/dev/docs/nextcloud_outputs.md new file mode 100644 index 0000000..1a8707b --- /dev/null +++ b/generated/dev/docs/nextcloud_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/nextcloud_params.md b/generated/dev/docs/nextcloud_params.md new file mode 100644 index 0000000..2c1eb71 --- /dev/null +++ b/generated/dev/docs/nextcloud_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Parameters + +- [Create params](nextcloud_params_create.md) +- [Modify params](nextcloud_params_modify.md) diff --git a/generated/dev/docs/nextcloud_params_create.md b/generated/dev/docs/nextcloud_params_create.md new file mode 100644 index 0000000..b3e6a19 --- /dev/null +++ b/generated/dev/docs/nextcloud_params_create.md @@ -0,0 +1,43 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
564psql_uiduuidref_svc_id=90
570redis_uiduuidref_svc_id=91
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
21domainstringweb01Наименование домена. Если будет указан FQDN, то происходят проверки DNS. Если указан поддомен, то будет добавлен в зону services.ngcloud.ru
Можно указать клиентскую DNS-Запись. Важно, чтобы запись смотрела на Ingress кластера
regex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
99resource_cpuinteger1000Квота ядра пода (Указывается в mili). 1 ядро = 1000 Miliminvalue=100; maxvalue=4000
100resource_memoryinteger1024Квота памяти пода (Указывается в Megabytes)minvalue=1024; maxvalue=8192
101resource_diskinteger2Квота диска (Указывается в GIGAbytes)minvalue=2; maxvalue=999
192document_serverstringcollaboraВыбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступенvalue_list=collabora
470resource_instancesinteger1Количество узлов
Не может пока быть больше одного, так как нужны SC под RWMany
minvalue=1; maxvalue=7; value_list=1
569app_version32.0.1value_list=32.0.1
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/nextcloud_params_modify.md b/generated/dev/docs/nextcloud_params_modify.md new file mode 100644 index 0000000..4da066d --- /dev/null +++ b/generated/dev/docs/nextcloud_params_modify.md @@ -0,0 +1,19 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Modify params + + + + + + + + + +
IDCodeTypeDescriptionConstraints
178resource_cpuintegerКвота ядра пода (Указывается в mili)minvalue=100; maxvalue=4000
179resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
180resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
547resource_instancesintegerКоличество узлов.
Не может пока быть больше одного, так как нужны SC под RWMany
minvalue=1; maxvalue=7; value_list=1
571app_versionstringНельзя обновляться сразу через несколько версий сразуvalue_list=32.0.1
diff --git a/generated/dev/docs/nifi.md b/generated/dev/docs/nifi.md new file mode 100644 index 0000000..b4f0531 --- /dev/null +++ b/generated/dev/docs/nifi.md @@ -0,0 +1,13 @@ +# Resource nubes_nifi + +Service ID: `153` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/nifi_example.md b/generated/dev/docs/nifi_example.md new file mode 100644 index 0000000..2da1321 --- /dev/null +++ b/generated/dev/docs/nifi_example.md @@ -0,0 +1,52 @@ +# Resource nubes_nifi + +Service ID: `153` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Copy-ready manifest (`nifi_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nifi" "baza" { + resource_realm = "TODO" # Кластер Kubernetes в котором будет развёрнуто приложение + jdbc_drivers = "TODO" # Пример: `https://jdbc.postgresql.org/download/postgresql-42.7.8.jar` + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "nifi-" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 2000 # Пример: `2000` + resource_memory = 4096 # Пример: `2048` + resource_instances = 1 # Пример: `1` + resource_disk = 10 # Пример: `10` + app_version = 2.6.0 # Выбрать из списка +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nifi.baza.state_params["имя_ключа"] +nubes_nifi.baza.state_out["имя_ключа"] +nubes_nifi.baza.state_params_flat["имя_ключа"] +nubes_nifi.baza.state_out_flat["имя_ключа"] +nubes_nifi.baza.vault_secrets["имя_ключа"] +nubes_nifi.baza.vault_url +nubes_nifi.baza.vault_user_path +nubes_nifi.baza.vault_fields +``` diff --git a/generated/dev/docs/nifi_ops.md b/generated/dev/docs/nifi_ops.md new file mode 100644 index 0000000..f135d36 --- /dev/null +++ b/generated/dev/docs/nifi_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_nifi + +Service ID: `153` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nifi_params_create.md) +- `delete` diff --git a/generated/dev/docs/nifi_outputs.md b/generated/dev/docs/nifi_outputs.md new file mode 100644 index 0000000..d3c1519 --- /dev/null +++ b/generated/dev/docs/nifi_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nifi + +Service ID: `153` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/nifi_params.md b/generated/dev/docs/nifi_params.md new file mode 100644 index 0000000..35b668c --- /dev/null +++ b/generated/dev/docs/nifi_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nifi + +Service ID: `153` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Parameters + +- [Create params](nifi_params_create.md) +- [Modify params](nifi_params_modify.md) diff --git a/generated/dev/docs/nifi_params_create.md b/generated/dev/docs/nifi_params_create.md new file mode 100644 index 0000000..04fd9b0 --- /dev/null +++ b/generated/dev/docs/nifi_params_create.md @@ -0,0 +1,30 @@ +# Resource nubes_nifi + +Service ID: `153` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
717resource_realmstringВыбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы
func=getAvailableResourceRealms
718jdbc_driversarray-map-fixedПример: `https://jdbc.postgresql.org/download/postgresql-42.7.8.jar`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
711domainstringnifi-Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
712resource_cpuinteger2000Указывается в Milicores
`1000` Mili == `1` Ядро
minvalue=2000; maxvalue=8000
713resource_memoryinteger4096Указывается в Megabytesminvalue=2048; maxvalue=16384
714resource_instancesinteger1Пример: `1`minvalue=1; maxvalue=1; value_list=1
715resource_diskinteger10Указывается в Gigabytesminvalue=1; maxvalue=999
716app_versionstring2.6.0Выбрать из спискаvalue_list=2.6.0
diff --git a/generated/dev/docs/nifi_params_modify.md b/generated/dev/docs/nifi_params_modify.md new file mode 100644 index 0000000..9b682b0 --- /dev/null +++ b/generated/dev/docs/nifi_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_nifi + +Service ID: `153` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/nodejs.md b/generated/dev/docs/nodejs.md new file mode 100644 index 0000000..a1c6804 --- /dev/null +++ b/generated/dev/docs/nodejs.md @@ -0,0 +1,13 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## MAN + +
+Развертывает контейнер в k8s кластере с node, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. +
diff --git a/generated/dev/docs/nodejs_example.md b/generated/dev/docs/nodejs_example.md new file mode 100644 index 0000000..9196079 --- /dev/null +++ b/generated/dev/docs/nodejs_example.md @@ -0,0 +1,51 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Copy-ready manifest (`nodejs_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nodejs" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + git_path = "https://github.com/xahys/testnode.git" # Путь до Git, который будет webroot для сайта + resource_cpu = 500 # Квота ядра пода (milicores). 1000Mili = 1 Ядро + resource_memory = 1024 # Квота памяти пода (MBytes) + resource_instances = 1 # Количество узлов + app_version = 23 # Версия приложения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nodejs.baza.state_params["имя_ключа"] +nubes_nodejs.baza.state_out["имя_ключа"] +nubes_nodejs.baza.state_params_flat["имя_ключа"] +nubes_nodejs.baza.state_out_flat["имя_ключа"] +nubes_nodejs.baza.vault_secrets["имя_ключа"] +nubes_nodejs.baza.vault_url +nubes_nodejs.baza.vault_user_path +nubes_nodejs.baza.vault_fields +``` diff --git a/generated/dev/docs/nodejs_ops.md b/generated/dev/docs/nodejs_ops.md new file mode 100644 index 0000000..de1e172 --- /dev/null +++ b/generated/dev/docs/nodejs_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nodejs_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](nodejs_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/dev/docs/nodejs_outputs.md b/generated/dev/docs/nodejs_outputs.md new file mode 100644 index 0000000..5fa8a14 --- /dev/null +++ b/generated/dev/docs/nodejs_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/dev/docs/nodejs_params.md b/generated/dev/docs/nodejs_params.md new file mode 100644 index 0000000..daf96ca --- /dev/null +++ b/generated/dev/docs/nodejs_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Parameters + +- [Create params](nodejs_params_create.md) +- [Modify params](nodejs_params_modify.md) diff --git a/generated/dev/docs/nodejs_params_create.md b/generated/dev/docs/nodejs_params_create.md new file mode 100644 index 0000000..1bf4766 --- /dev/null +++ b/generated/dev/docs/nodejs_params_create.md @@ -0,0 +1,43 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
151health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет
152json_envjsonМассив переменных, будут переданы в env приложения
156resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
149domainstringweb01Наименование поддомена, будет добавлено в nodejs.dev.nubes.ruregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
150git_pathstringhttps://github.com/xahys/testnode.gitПуть до Git, который будет webroot для сайта
153resource_cpuinteger500Квота ядра пода (milicores). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
154resource_memoryinteger1024Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
155resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
270app_versionstring23Версия приложенияregex=^(\d){2}$; value_list=18, 20, 22, 23
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/nodejs_params_modify.md b/generated/dev/docs/nodejs_params_modify.md new file mode 100644 index 0000000..89a753e --- /dev/null +++ b/generated/dev/docs/nodejs_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
157git_pathstringПуть до Git, который будет webroot для сайта
158health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код.
159json_envjsonМассив переменных, будут переданы в env приложенияmaxlength=2048
160resource_cpuintegerКвота ядра пода (Указывается в mili) 1000Mili = 1 ядроminvalue=100; maxvalue=4000
161resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
162resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
271app_versionstringВерсия приложенияregex=^(\d){2}$; value_list=18, 20, 22, 23
diff --git a/generated/dev/docs/nodered.md b/generated/dev/docs/nodered.md new file mode 100644 index 0000000..54ecff3 --- /dev/null +++ b/generated/dev/docs/nodered.md @@ -0,0 +1,13 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/nodered_example.md b/generated/dev/docs/nodered_example.md new file mode 100644 index 0000000..6a5a7f5 --- /dev/null +++ b/generated/dev/docs/nodered_example.md @@ -0,0 +1,50 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Copy-ready manifest (`nodered_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nodered" "baza" { + resource_realm = "TODO" # Платформа для развертывания + domain = "TODO" # Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nodered.baza.state_params["имя_ключа"] +nubes_nodered.baza.state_out["имя_ключа"] +nubes_nodered.baza.state_params_flat["имя_ключа"] +nubes_nodered.baza.state_out_flat["имя_ключа"] +nubes_nodered.baza.vault_secrets["имя_ключа"] +nubes_nodered.baza.vault_url +nubes_nodered.baza.vault_user_path +nubes_nodered.baza.vault_fields +``` diff --git a/generated/dev/docs/nodered_ops.md b/generated/dev/docs/nodered_ops.md new file mode 100644 index 0000000..2d6074e --- /dev/null +++ b/generated/dev/docs/nodered_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nodered_params_create.md) +- `delete` diff --git a/generated/dev/docs/nodered_outputs.md b/generated/dev/docs/nodered_outputs.md new file mode 100644 index 0000000..74e4213 --- /dev/null +++ b/generated/dev/docs/nodered_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/nodered_params.md b/generated/dev/docs/nodered_params.md new file mode 100644 index 0000000..60a797d --- /dev/null +++ b/generated/dev/docs/nodered_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Parameters + +- [Create params](nodered_params_create.md) +- [Modify params](nodered_params_modify.md) diff --git a/generated/dev/docs/nodered_params_create.md b/generated/dev/docs/nodered_params_create.md new file mode 100644 index 0000000..f976ccb --- /dev/null +++ b/generated/dev/docs/nodered_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
196resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
197domainstringНаименование поддомена, будет добавлено в k8stemplate.dev.nubes.ruregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
193resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
194resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
195resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
323resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
diff --git a/generated/dev/docs/nodered_params_modify.md b/generated/dev/docs/nodered_params_modify.md new file mode 100644 index 0000000..be00f3b --- /dev/null +++ b/generated/dev/docs/nodered_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/pgadmin.md b/generated/dev/docs/pgadmin.md new file mode 100644 index 0000000..5c2feaf --- /dev/null +++ b/generated/dev/docs/pgadmin.md @@ -0,0 +1,13 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## MAN + +
+Инструмент с графическим интерфейсом для управления базами данных PostgreSQL +
diff --git a/generated/dev/docs/pgadmin_example.md b/generated/dev/docs/pgadmin_example.md new file mode 100644 index 0000000..ec4edc2 --- /dev/null +++ b/generated/dev/docs/pgadmin_example.md @@ -0,0 +1,51 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Copy-ready manifest (`pgadmin_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_pgadmin" "baza" { + resource_realm = "TODO" # Платформа для развертывания + login = "TODO" # Логин для подключения к pgAdmin, в формате email (login@email.net) + password = "TODO" # Пароль для подключения к pgAdmin + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 500 # Квота ядра пода (milicores). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_pgadmin.baza.state_params["имя_ключа"] +nubes_pgadmin.baza.state_out["имя_ключа"] +nubes_pgadmin.baza.state_params_flat["имя_ключа"] +nubes_pgadmin.baza.state_out_flat["имя_ключа"] +nubes_pgadmin.baza.vault_secrets["имя_ключа"] +nubes_pgadmin.baza.vault_url +nubes_pgadmin.baza.vault_user_path +nubes_pgadmin.baza.vault_fields +``` diff --git a/generated/dev/docs/pgadmin_ops.md b/generated/dev/docs/pgadmin_ops.md new file mode 100644 index 0000000..7449f5e --- /dev/null +++ b/generated/dev/docs/pgadmin_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](pgadmin_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](pgadmin_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/dev/docs/pgadmin_outputs.md b/generated/dev/docs/pgadmin_outputs.md new file mode 100644 index 0000000..a88297a --- /dev/null +++ b/generated/dev/docs/pgadmin_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/dev/docs/pgadmin_params.md b/generated/dev/docs/pgadmin_params.md new file mode 100644 index 0000000..3afd95c --- /dev/null +++ b/generated/dev/docs/pgadmin_params.md @@ -0,0 +1,12 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Parameters + +- [Create params](pgadmin_params_create.md) +- [Modify params](pgadmin_params_modify.md) diff --git a/generated/dev/docs/pgadmin_params_create.md b/generated/dev/docs/pgadmin_params_create.md new file mode 100644 index 0000000..f87da9c --- /dev/null +++ b/generated/dev/docs/pgadmin_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
169resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
170loginstringЛогин для подключения к pgAdmin, в формате email (login@email.net)regex=^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$; maxlength=255; minlength=3
171passwordstringПароль для подключения к pgAdmin
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
164domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
165resource_cpuinteger500Квота ядра пода (milicores). 1000Mili = 1 Ядроminvalue=500; maxvalue=4000
166resource_memoryinteger512Квота памяти пода (MBytes)minvalue=512; maxvalue=8192
167resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/pgadmin_params_modify.md b/generated/dev/docs/pgadmin_params_modify.md new file mode 100644 index 0000000..0b7613e --- /dev/null +++ b/generated/dev/docs/pgadmin_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
172resource_cpuintegerКвота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
173resource_memoryintegerКвота памяти пода (MBytes)minvalue=128; maxvalue=8192
174resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
diff --git a/generated/dev/docs/postgres.md b/generated/dev/docs/postgres.md new file mode 100644 index 0000000..61e7668 --- /dev/null +++ b/generated/dev/docs/postgres.md @@ -0,0 +1,13 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## MAN + +
+# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
+
diff --git a/generated/dev/docs/postgres_example.md b/generated/dev/docs/postgres_example.md new file mode 100644 index 0000000..8a60f95 --- /dev/null +++ b/generated/dev/docs/postgres_example.md @@ -0,0 +1,63 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Copy-ready manifest (`postgres_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_postgres" "baza" { + s3_uid = "TODO" # Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_instances = 1 # Количество узлов + resource_memory = 512 # Квота памяти пода (MBytes) + resource_cpu = 500 # Квота ядра пода (milicores) + resource_disk = 10 # Квота диска (Указывается в GIGAbytes) + need_external_address_master = false # Выделяет внешний (белый) IP для доступа к master + need_external_address_slave = false # Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть + ext_backup_schedule = "0 0 * * *" # Не обязательный. Время запуска бекапа + ext_backup_num_to_retain = 7 # Не обязательный. Количество сохраняемых бекапов + app_version = 17 # Версия PostgreSQL + json_parameters = { "log_connections": "off", "log_disconnections": "off" } # Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html + enable_pg_pooler_master = false # Включает pgPooler (на запись). Будет создан новый под + enable_pg_pooler_slave = false # Включает pgPooler (на чтение). Будет создан новый под + allow_no_ssl = false # По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр + auto_scale = false # Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + auto_scale_percentage = 10 # Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + auto_scale_tech_window = 0 # Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + auto_scale_quota_gb = 1 # Квота для autoScale +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_postgres.baza.state_params["имя_ключа"] +nubes_postgres.baza.state_out["имя_ключа"] +nubes_postgres.baza.state_params_flat["имя_ключа"] +nubes_postgres.baza.state_out_flat["имя_ключа"] +nubes_postgres.baza.vault_secrets["имя_ключа"] +nubes_postgres.baza.vault_url +nubes_postgres.baza.vault_user_path +nubes_postgres.baza.vault_fields +``` diff --git a/generated/dev/docs/postgres_ops.md b/generated/dev/docs/postgres_ops.md new file mode 100644 index 0000000..601c4bd --- /dev/null +++ b/generated/dev/docs/postgres_ops.md @@ -0,0 +1,21 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](postgres_params_create.md) +- `create_database` — Создание кластера. Параметры: [Create params](postgres_params_create.md) +- `create_user` — Создание кластера. Параметры: [Create params](postgres_params_create.md) +- `delete` +- `delete_database` +- `delete_user` +- `modify` — Изменение кластера. Параметры: [Modify params](postgres_params_modify.md) +- `recovery` +- `restart` — ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов. Может быть полная поломка. Крайне осторожно запускать при работе +- `resume` +- `suspend` diff --git a/generated/dev/docs/postgres_outputs.md b/generated/dev/docs/postgres_outputs.md new file mode 100644 index 0000000..f07b697 --- /dev/null +++ b/generated/dev/docs/postgres_outputs.md @@ -0,0 +1,65 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.master"]` +- `state_out_flat["externalConnect.master.fqdn"]` +- `state_out_flat["externalConnect.master.ip"]` +- `state_out_flat["externalConnect.master.isExternal"]` +- `state_out_flat["externalConnect.master.uuid"]` +- `state_out_flat["externalConnect.slave"]` +- `state_out_flat["externalConnect.slave.fqdn"]` +- `state_out_flat["externalConnect.slave.ip"]` +- `state_out_flat["externalConnect.slave.isExternal"]` +- `state_out_flat["externalConnect.slave.uuid"]` +- `state_out_flat["internalConnect"]` +- `state_out_flat["internalConnect.master"]` +- `state_out_flat["internalConnect.slave"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.k8spostgresql"]` +- `state_out_flat["monitoring.k8spostgresql.url"]` +- `state_out_flat["monitoring.k8spostgresqlbackup"]` +- `state_out_flat["monitoring.k8spostgresqlbackup.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` +- `vault_secrets["standbyPass"]` +- `vault_secrets["standbyUser"]` + +### Пример для связки с Lucee/NodeJS + +```hcl +testds_connectionString = "jdbc:postgresql://${nubes_postgres.db2.state_out_flat["internalConnect.master"]}:5432/postgres?sslmode=require" +testds_username = nubes_postgres.db2.vault_secrets["adminUser"] +testds_password = nubes_postgres.db2.vault_secrets["adminPass"] +``` diff --git a/generated/dev/docs/postgres_params.md b/generated/dev/docs/postgres_params.md new file mode 100644 index 0000000..b44e9f4 --- /dev/null +++ b/generated/dev/docs/postgres_params.md @@ -0,0 +1,12 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Parameters + +- [Create params](postgres_params_create.md) +- [Modify params](postgres_params_modify.md) diff --git a/generated/dev/docs/postgres_params_create.md b/generated/dev/docs/postgres_params_create.md new file mode 100644 index 0000000..2eb0637 --- /dev/null +++ b/generated/dev/docs/postgres_params_create.md @@ -0,0 +1,55 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
23s3_uiduuidЭкземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательноref_svc_id=12
102resource_realmstringКластер Kubernetes, на котором будет развернут экземплярfunc=getAvailableResourceRealms
337ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`
338ip_space_name_slavestringИмя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
80resource_instancesinteger1Количество узлов в кластере. 1,3,5,7minvalue=1; maxvalue=7; regex=^[1357]$
81resource_memoryinteger512Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
82resource_cpuinteger500Квота ядра пода (milicores)minvalue=100; maxvalue=4000
83resource_disk10Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
145need_external_address_masterbooleanfalseВыделяет внешний (белый) IP для доступа к mastervalue_list=false, true
146need_external_address_slavebooleanfalseВыделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе естьvalue_list=false, true
265ext_backup_schedulestring0 0 * * *Не обязательный. Время запуска бекапаregex=^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$
266ext_backup_num_to_retaininteger7Не обязательный. Количество сохраняемых бекаповminvalue=1; maxvalue=99
310app_versionstring17Версия PostgreSQLvalue_list=17, 16
311json_parametersjson{ "log_connections": "off", "log_disconnections": "off" }Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.htmlmaxlength=2048
312enable_pg_pooler_masterbooleanfalseВключает pgPooler (на запись). Будет создан новый подvalue_list=false, true
313enable_pg_pooler_slavebooleanfalseВключает pgPooler (на чтение). Будет создан новый подvalue_list=false, true
314allow_no_sslbooleanfalseПо умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметрvalue_list=false, true
329auto_scalebooleanfalseВключает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услугиvalue_list=false, true
330auto_scale_percentageinteger10Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объемаvalue_list=10, 15, 20
331auto_scale_tech_windowinteger0Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)value_list=0, 5, 12, 23
339auto_scale_quota_gbstring1Квота для autoScale
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/postgres_params_modify.md b/generated/dev/docs/postgres_params_modify.md new file mode 100644 index 0000000..a61d69e --- /dev/null +++ b/generated/dev/docs/postgres_params_modify.md @@ -0,0 +1,29 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Modify params + + + + + + + + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
91resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
92resource_memoryintegerКвота памяти пода (MBytes)minvalue=128; maxvalue=8192
93resource_cpuintegerКвота ядра пода (milicores)minvalue=100; maxvalue=4000
94resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
147need_external_address_masterbooleanВыделяет внешний IP для доступа к mastervalue_list=false, true
148need_external_address_slavebooleanВыделяет внешний IP для доступа к slave. Действует, если slave в принципе естьvalue_list=false, true
267ext_backup_schedulestringНе обязательный. Время запуска бекапаregex=^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$
268ext_backup_num_to_retainintegerНе обязательный. Количество сохраняемых бекаповminvalue=1; maxvalue=99
315app_versionstringВерсия PostgreSQL. Нельзя опускать внизvalue_list=17, 16
316json_parametersjsonМассив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.htmlmaxlength=2048
317enable_pg_pooler_masterbooleanВключает pgPooler (на запись). Будет создан новый подvalue_list=false, true
318enable_pg_pooler_slavebooleanВключает pgPooler (на чтение). Будет создан новый подvalue_list=false, true
319allow_no_sslbooleanПо умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметрvalue_list=false, true
541ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`
542ip_space_name_slavestringИмя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave
diff --git a/generated/dev/docs/rabbitmq.md b/generated/dev/docs/rabbitmq.md new file mode 100644 index 0000000..d0e5af3 --- /dev/null +++ b/generated/dev/docs/rabbitmq.md @@ -0,0 +1,13 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## MAN + +
+# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

--- +
diff --git a/generated/dev/docs/rabbitmq_example.md b/generated/dev/docs/rabbitmq_example.md new file mode 100644 index 0000000..b5fc1fe --- /dev/null +++ b/generated/dev/docs/rabbitmq_example.md @@ -0,0 +1,50 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Copy-ready manifest (`rabbitmq_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_rabbitmq" "baza" { + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 1000 # Пример: `2000` + resource_memory = 1124 # Пример: `2048` + resource_disk = 30 # Пример: `20` + resource_instances = 1 # Пример: `1` + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_rabbitmq.baza.state_params["имя_ключа"] +nubes_rabbitmq.baza.state_out["имя_ключа"] +nubes_rabbitmq.baza.state_params_flat["имя_ключа"] +nubes_rabbitmq.baza.state_out_flat["имя_ключа"] +nubes_rabbitmq.baza.vault_secrets["имя_ключа"] +nubes_rabbitmq.baza.vault_url +nubes_rabbitmq.baza.vault_user_path +nubes_rabbitmq.baza.vault_fields +``` diff --git a/generated/dev/docs/rabbitmq_ops.md b/generated/dev/docs/rabbitmq_ops.md new file mode 100644 index 0000000..fe63cf6 --- /dev/null +++ b/generated/dev/docs/rabbitmq_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](rabbitmq_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](rabbitmq_params_modify.md) +- `resume` +- `suspend` — Пока не работает из за того что RabbitmqCluster нельзя занулить diff --git a/generated/dev/docs/rabbitmq_outputs.md b/generated/dev/docs/rabbitmq_outputs.md new file mode 100644 index 0000000..30d870e --- /dev/null +++ b/generated/dev/docs/rabbitmq_outputs.md @@ -0,0 +1,48 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["clusterDomain"]` +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.admin"]` +- `state_out_flat["externalConnect.admin.fqdn"]` +- `state_out_flat["externalConnect.admin.ip"]` +- `state_out_flat["externalConnect.admin.isExternal"]` +- `state_out_flat["externalConnect.admin.uuid"]` +- `state_out_flat["inernalConnect"]` +- `state_out_flat["inernalConnect.master"]` +- `state_out_flat["inernalConnect.slave"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` + diff --git a/generated/dev/docs/rabbitmq_params.md b/generated/dev/docs/rabbitmq_params.md new file mode 100644 index 0000000..b743d18 --- /dev/null +++ b/generated/dev/docs/rabbitmq_params.md @@ -0,0 +1,12 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Parameters + +- [Create params](rabbitmq_params_create.md) +- [Modify params](rabbitmq_params_modify.md) diff --git a/generated/dev/docs/rabbitmq_params_create.md b/generated/dev/docs/rabbitmq_params_create.md new file mode 100644 index 0000000..1a1b21f --- /dev/null +++ b/generated/dev/docs/rabbitmq_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
119resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
543ip_space_name_masterstringПример: `internet-no-antiddos-v1`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
116resource_cpuinteger1000Пример: `2000`minvalue=500; maxvalue=4000
117resource_memoryinteger1124Пример: `2048`minvalue=512; maxvalue=8192
118resource_diskinteger30Пример: `20`minvalue=5; maxvalue=999
224resource_instancesinteger1Пример: `1`minvalue=1; maxvalue=7
225need_external_address_masterbooleanfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`value_list=false, true
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/rabbitmq_params_modify.md b/generated/dev/docs/rabbitmq_params_modify.md new file mode 100644 index 0000000..6df624c --- /dev/null +++ b/generated/dev/docs/rabbitmq_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
304resource_cpuintegerПример: `2000`minvalue=500; maxvalue=4000
305resource_memoryintegerПример: `2048`minvalue=512; maxvalue=8192
306resource_diskintegerПример: `20`minvalue=1; maxvalue=999
307resource_instancesintegerПример: `1`minvalue=1; maxvalue=7
308need_external_address_masterbooleanВыделяет внешний (белый) IP для доступа к mastervalue_list=false, true
545ip_space_name_masterstringПример: `internet-no-antiddos-v1`
diff --git a/generated/dev/docs/redis.md b/generated/dev/docs/redis.md new file mode 100644 index 0000000..4dfd2ea --- /dev/null +++ b/generated/dev/docs/redis.md @@ -0,0 +1,13 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## MAN + +
+Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379 +
diff --git a/generated/dev/docs/redis_example.md b/generated/dev/docs/redis_example.md new file mode 100644 index 0000000..3e52da0 --- /dev/null +++ b/generated/dev/docs/redis_example.md @@ -0,0 +1,51 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Copy-ready manifest (`redis_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_redis" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 1000 # Квота ядра пода (Указывается в mili) + resource_memory = 1024 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 30 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + need_external_address_master = false # Выделяет внешний IP для доступа к master + need_external_address_slave = false # Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_redis.baza.state_params["имя_ключа"] +nubes_redis.baza.state_out["имя_ключа"] +nubes_redis.baza.state_params_flat["имя_ключа"] +nubes_redis.baza.state_out_flat["имя_ключа"] +nubes_redis.baza.vault_secrets["имя_ключа"] +nubes_redis.baza.vault_url +nubes_redis.baza.vault_user_path +nubes_redis.baza.vault_fields +``` diff --git a/generated/dev/docs/redis_ops.md b/generated/dev/docs/redis_ops.md new file mode 100644 index 0000000..d5d813c --- /dev/null +++ b/generated/dev/docs/redis_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](redis_params_create.md) +- `delete` diff --git a/generated/dev/docs/redis_outputs.md b/generated/dev/docs/redis_outputs.md new file mode 100644 index 0000000..c735f67 --- /dev/null +++ b/generated/dev/docs/redis_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/redis_params.md b/generated/dev/docs/redis_params.md new file mode 100644 index 0000000..131dd5e --- /dev/null +++ b/generated/dev/docs/redis_params.md @@ -0,0 +1,12 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Parameters + +- [Create params](redis_params_create.md) +- [Modify params](redis_params_modify.md) diff --git a/generated/dev/docs/redis_params_create.md b/generated/dev/docs/redis_params_create.md new file mode 100644 index 0000000..2dccb7e --- /dev/null +++ b/generated/dev/docs/redis_params_create.md @@ -0,0 +1,31 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
182resource_realmstringНаименование кластера Kubernetes для подключения
func=getAvailableResourceRealms
548ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster
549ip_space_name_slavestringИмя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
112resource_cpuinteger1000Квота ядра пода (Указывается в mili)minvalue=500; maxvalue=4000
113resource_memoryinteger1024Квота памяти пода (Указывается в Megabytes)minvalue=512; maxvalue=8192
114resource_diskinteger30Квота диска (Указывается в GIGAbytes)minvalue=10; maxvalue=999
181resource_instancesinteger1Количество узлов в сети. Если выделить больше 1 ноды, будет добавлено дополнительно 3 Sentinel нодыminvalue=1; maxvalue=7
183need_external_address_masterbooleanfalseВыделяет внешний IP для доступа к mastervalue_list=false, true
184need_external_address_slavebooleanfalseВыделяет внешний IP для доступа к slave. Будет работать если slave в целом естьvalue_list=false, true
diff --git a/generated/dev/docs/redis_params_modify.md b/generated/dev/docs/redis_params_modify.md new file mode 100644 index 0000000..7461280 --- /dev/null +++ b/generated/dev/docs/redis_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/s3.md b/generated/dev/docs/s3.md new file mode 100644 index 0000000..15a87a0 --- /dev/null +++ b/generated/dev/docs/s3.md @@ -0,0 +1,13 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## MAN + +
+Руководство доступно по ссылке:
https://docs.s3.msk-1.ngcloud.ru/s3/s3.description.html +
diff --git a/generated/dev/docs/s3_example.md b/generated/dev/docs/s3_example.md new file mode 100644 index 0000000..c182be4 --- /dev/null +++ b/generated/dev/docs/s3_example.md @@ -0,0 +1,44 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Copy-ready manifest (`s3_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_s3" "baza" { + resource_realm = "TODO" # Платформа для развертывания + display_name = "TODO" # Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_s3.baza.state_params["имя_ключа"] +nubes_s3.baza.state_out["имя_ключа"] +nubes_s3.baza.state_params_flat["имя_ключа"] +nubes_s3.baza.state_out_flat["имя_ключа"] +nubes_s3.baza.vault_secrets["имя_ключа"] +nubes_s3.baza.vault_url +nubes_s3.baza.vault_user_path +nubes_s3.baza.vault_fields +``` diff --git a/generated/dev/docs/s3_ops.md b/generated/dev/docs/s3_ops.md new file mode 100644 index 0000000..d030e69 --- /dev/null +++ b/generated/dev/docs/s3_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](s3_params_create.md) +- `create_sub_user` — Создание кластера. Параметры: [Create params](s3_params_create.md) +- `delete` +- `delete_sub_user` +- `modify` — Изменение кластера. Параметры: [Modify params](s3_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/s3_outputs.md b/generated/dev/docs/s3_outputs.md new file mode 100644 index 0000000..e1cdeb2 --- /dev/null +++ b/generated/dev/docs/s3_outputs.md @@ -0,0 +1,42 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dtEndTrial"]` +- `state_out_flat["dtStartTrial"]` +- `state_out_flat["isTrial"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.s3ceph"]` +- `state_out_flat["monitoring.s3ceph.url"]` +- `state_out_flat["urlConnect"]` + +### `vault_secrets` ключи + +- `vault_secrets["1325_access_key"]` +- `vault_secrets["1325_secret_key"]` +- `vault_secrets["1325_technicalS3Backup_access_key"]` +- `vault_secrets["1325_technicalS3Backup_secret_key"]` + diff --git a/generated/dev/docs/s3_params.md b/generated/dev/docs/s3_params.md new file mode 100644 index 0000000..d61aced --- /dev/null +++ b/generated/dev/docs/s3_params.md @@ -0,0 +1,12 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Parameters + +- [Create params](s3_params_create.md) +- [Modify params](s3_params_modify.md) diff --git a/generated/dev/docs/s3_params_create.md b/generated/dev/docs/s3_params_create.md new file mode 100644 index 0000000..743e44f --- /dev/null +++ b/generated/dev/docs/s3_params_create.md @@ -0,0 +1,33 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
163resource_realmкластер S3 для подключенияfunc=getAvailableResourceRealms
320display_namestringЧеловеко-читаемое имя (транслит из Elma) - передаётся имя клиента
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/s3_params_modify.md b/generated/dev/docs/s3_params_modify.md new file mode 100644 index 0000000..d5a1da0 --- /dev/null +++ b/generated/dev/docs/s3_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
50max_size_gb_per_userintegermaxSizeGb. Передаём рукамиminvalue=1; maxvalue=50000
51max_objects_per_bucketintegerКвота на бакет. Кол-во объектов. Передаём рукамиminvalue=1; maxvalue=10000000
52max_buckets_per_userintegerКвота на пользователя. Количество бакетов пользователя. Передаём рукамиminvalue=1; maxvalue=1000
diff --git a/generated/dev/docs/s3bucket.md b/generated/dev/docs/s3bucket.md new file mode 100644 index 0000000..27d17d7 --- /dev/null +++ b/generated/dev/docs/s3bucket.md @@ -0,0 +1,13 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/s3bucket_example.md b/generated/dev/docs/s3bucket_example.md new file mode 100644 index 0000000..a05fdcd --- /dev/null +++ b/generated/dev/docs/s3bucket_example.md @@ -0,0 +1,51 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Copy-ready manifest (`s3bucket_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_s3bucket" "baza" { + s3_user_uid = "TODO" # UID Корневой услуги S3. На основе него будет создан бакет. + bucket_name = "TODO" # Имя бакета s3 + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + max_size = -1 # Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes + read_all = false # Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. + list_all = false # Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. + cors_all = false # Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS + placement = "HOT" # Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_s3bucket.baza.state_params["имя_ключа"] +nubes_s3bucket.baza.state_out["имя_ключа"] +nubes_s3bucket.baza.state_params_flat["имя_ключа"] +nubes_s3bucket.baza.state_out_flat["имя_ключа"] +nubes_s3bucket.baza.vault_secrets["имя_ключа"] +nubes_s3bucket.baza.vault_url +nubes_s3bucket.baza.vault_user_path +nubes_s3bucket.baza.vault_fields +``` diff --git a/generated/dev/docs/s3bucket_ops.md b/generated/dev/docs/s3bucket_ops.md new file mode 100644 index 0000000..61b2abb --- /dev/null +++ b/generated/dev/docs/s3bucket_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](s3bucket_params_create.md) +- `delete` diff --git a/generated/dev/docs/s3bucket_outputs.md b/generated/dev/docs/s3bucket_outputs.md new file mode 100644 index 0000000..f62a754 --- /dev/null +++ b/generated/dev/docs/s3bucket_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["httpConnect"]` + diff --git a/generated/dev/docs/s3bucket_params.md b/generated/dev/docs/s3bucket_params.md new file mode 100644 index 0000000..9977e26 --- /dev/null +++ b/generated/dev/docs/s3bucket_params.md @@ -0,0 +1,12 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Parameters + +- [Create params](s3bucket_params_create.md) +- [Modify params](s3bucket_params_modify.md) diff --git a/generated/dev/docs/s3bucket_params_create.md b/generated/dev/docs/s3bucket_params_create.md new file mode 100644 index 0000000..0ae6f44 --- /dev/null +++ b/generated/dev/docs/s3bucket_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
124s3_user_uiduuidUID Корневой услуги S3. На основе него будет создан бакет.ref_svc_id=12
125bucket_namestringИмя бакета s3
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
126max_size-1Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes
127read_allbooleanfalseПубличный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета.value_list=false, true
128list_allbooleanfalseПубличный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете.value_list=false, true
129cors_allbooleanfalseПубличный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORSvalue_list=false, true
130placementstringHOTКласс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращенияvalue_list=HOT, COLD
diff --git a/generated/dev/docs/s3bucket_params_modify.md b/generated/dev/docs/s3bucket_params_modify.md new file mode 100644 index 0000000..ca24765 --- /dev/null +++ b/generated/dev/docs/s3bucket_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/superset.md b/generated/dev/docs/superset.md new file mode 100644 index 0000000..93094e9 --- /dev/null +++ b/generated/dev/docs/superset.md @@ -0,0 +1,13 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## MAN + +
+Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru) +
diff --git a/generated/dev/docs/superset_example.md b/generated/dev/docs/superset_example.md new file mode 100644 index 0000000..6abf626 --- /dev/null +++ b/generated/dev/docs/superset_example.md @@ -0,0 +1,51 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Copy-ready manifest (`superset_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_superset" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + emails = "testuser1" # Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_superset.baza.state_params["имя_ключа"] +nubes_superset.baza.state_out["имя_ключа"] +nubes_superset.baza.state_params_flat["имя_ключа"] +nubes_superset.baza.state_out_flat["имя_ключа"] +nubes_superset.baza.vault_secrets["имя_ключа"] +nubes_superset.baza.vault_url +nubes_superset.baza.vault_user_path +nubes_superset.baza.vault_fields +``` diff --git a/generated/dev/docs/superset_ops.md b/generated/dev/docs/superset_ops.md new file mode 100644 index 0000000..5aa441d --- /dev/null +++ b/generated/dev/docs/superset_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](superset_params_create.md) +- `delete` — DEV-Ветка удаления Superset diff --git a/generated/dev/docs/superset_outputs.md b/generated/dev/docs/superset_outputs.md new file mode 100644 index 0000000..4d7a1fd --- /dev/null +++ b/generated/dev/docs/superset_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/superset_params.md b/generated/dev/docs/superset_params.md new file mode 100644 index 0000000..2984f1c --- /dev/null +++ b/generated/dev/docs/superset_params.md @@ -0,0 +1,12 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Parameters + +- [Create params](superset_params_create.md) +- [Modify params](superset_params_modify.md) diff --git a/generated/dev/docs/superset_params_create.md b/generated/dev/docs/superset_params_create.md new file mode 100644 index 0000000..6652cf1 --- /dev/null +++ b/generated/dev/docs/superset_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
109resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
14domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
20emailsstringtestuser1Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел
96resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядроminvalue=100; maxvalue=4000
97resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
98resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
227resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
diff --git a/generated/dev/docs/superset_params_modify.md b/generated/dev/docs/superset_params_modify.md new file mode 100644 index 0000000..5a6e1ca --- /dev/null +++ b/generated/dev/docs/superset_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/template.md b/generated/dev/docs/template.md new file mode 100644 index 0000000..b4d0dc3 --- /dev/null +++ b/generated/dev/docs/template.md @@ -0,0 +1,13 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/template_example.md b/generated/dev/docs/template_example.md new file mode 100644 index 0000000..979b7dd --- /dev/null +++ b/generated/dev/docs/template_example.md @@ -0,0 +1,50 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Copy-ready manifest (`template_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_template" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 100 + resource_memory = 200 + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_template.baza.state_params["имя_ключа"] +nubes_template.baza.state_out["имя_ключа"] +nubes_template.baza.state_params_flat["имя_ключа"] +nubes_template.baza.state_out_flat["имя_ключа"] +nubes_template.baza.vault_secrets["имя_ключа"] +nubes_template.baza.vault_url +nubes_template.baza.vault_user_path +nubes_template.baza.vault_fields +``` diff --git a/generated/dev/docs/template_ops.md b/generated/dev/docs/template_ops.md new file mode 100644 index 0000000..7bea1f5 --- /dev/null +++ b/generated/dev/docs/template_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](template_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](template_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/template_outputs.md b/generated/dev/docs/template_outputs.md new file mode 100644 index 0000000..ab5623d --- /dev/null +++ b/generated/dev/docs/template_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/template_params.md b/generated/dev/docs/template_params.md new file mode 100644 index 0000000..7fdd8d3 --- /dev/null +++ b/generated/dev/docs/template_params.md @@ -0,0 +1,12 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Parameters + +- [Create params](template_params_create.md) +- [Modify params](template_params_modify.md) diff --git a/generated/dev/docs/template_params_create.md b/generated/dev/docs/template_params_create.md new file mode 100644 index 0000000..aaa56ea --- /dev/null +++ b/generated/dev/docs/template_params_create.md @@ -0,0 +1,40 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
206resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
202resource_cpuinteger100minvalue=100; maxvalue=4000
203resource_memoryinteger200minvalue=128; maxvalue=8192
204resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
205resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
207domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/template_params_modify.md b/generated/dev/docs/template_params_modify.md new file mode 100644 index 0000000..9e15054 --- /dev/null +++ b/generated/dev/docs/template_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
208resource_cpuintegerminvalue=100; maxvalue=4000
209resource_memoryintegerminvalue=128; maxvalue=8192
210resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
211resource_instancesintegerКоличество узловminvalue=1; maxvalue=7
diff --git a/generated/dev/docs/valo_tenant.md b/generated/dev/docs/valo_tenant.md new file mode 100644 index 0000000..6860e2f --- /dev/null +++ b/generated/dev/docs/valo_tenant.md @@ -0,0 +1,13 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## MAN + +
+# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
+
diff --git a/generated/dev/docs/valo_tenant_example.md b/generated/dev/docs/valo_tenant_example.md new file mode 100644 index 0000000..7f29a47 --- /dev/null +++ b/generated/dev/docs/valo_tenant_example.md @@ -0,0 +1,48 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Copy-ready manifest (`valo_tenant_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_valo_tenant" "baza" { + resource_realm = "TODO" + domain = "TODO" # Будет добавлено в зону valo.ngcloud.ru + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + valo_options = {} + s3_storage = 10 # Параметр указывается в `Gb` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_valo_tenant.baza.state_params["имя_ключа"] +nubes_valo_tenant.baza.state_out["имя_ключа"] +nubes_valo_tenant.baza.state_params_flat["имя_ключа"] +nubes_valo_tenant.baza.state_out_flat["имя_ключа"] +nubes_valo_tenant.baza.vault_secrets["имя_ключа"] +nubes_valo_tenant.baza.vault_url +nubes_valo_tenant.baza.vault_user_path +nubes_valo_tenant.baza.vault_fields +``` diff --git a/generated/dev/docs/valo_tenant_ops.md b/generated/dev/docs/valo_tenant_ops.md new file mode 100644 index 0000000..89cb168 --- /dev/null +++ b/generated/dev/docs/valo_tenant_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](valo_tenant_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](valo_tenant_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/dev/docs/valo_tenant_outputs.md b/generated/dev/docs/valo_tenant_outputs.md new file mode 100644 index 0000000..2d4ea5d --- /dev/null +++ b/generated/dev/docs/valo_tenant_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/valo_tenant_params.md b/generated/dev/docs/valo_tenant_params.md new file mode 100644 index 0000000..2c3d14f --- /dev/null +++ b/generated/dev/docs/valo_tenant_params.md @@ -0,0 +1,12 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Parameters + +- [Create params](valo_tenant_params_create.md) +- [Modify params](valo_tenant_params_modify.md) diff --git a/generated/dev/docs/valo_tenant_params_create.md b/generated/dev/docs/valo_tenant_params_create.md new file mode 100644 index 0000000..a0e9538 --- /dev/null +++ b/generated/dev/docs/valo_tenant_params_create.md @@ -0,0 +1,38 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
655resource_realmstringfunc=getAvailableResourceRealms
656domainstringБудет добавлено в зону valo.ngcloud.ruregex=^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$; maxlength=61; minlength=3
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
657valo_optionsmap-fixed{}
658s3_storageinteger10Данные хранятся в отдельном экземпляре S3minvalue=1; maxvalue=999
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/valo_tenant_params_modify.md b/generated/dev/docs/valo_tenant_params_modify.md new file mode 100644 index 0000000..4b07bec --- /dev/null +++ b/generated/dev/docs/valo_tenant_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
659valo_optionsmap-fixed
660s3_storageintegerДанные хранятся в отдельном экземпляре S3
diff --git a/generated/dev/docs/vapp.md b/generated/dev/docs/vapp.md new file mode 100644 index 0000000..630f47c --- /dev/null +++ b/generated/dev/docs/vapp.md @@ -0,0 +1,13 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## MAN + +
+# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции. +
diff --git a/generated/dev/docs/vapp_example.md b/generated/dev/docs/vapp_example.md new file mode 100644 index 0000000..cd9335b --- /dev/null +++ b/generated/dev/docs/vapp_example.md @@ -0,0 +1,45 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Copy-ready manifest (`vapp_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vapp" "baza" { + nsxt_uid = "TODO" # UUID Услуги `Сетевой шлюз периметра (Edge)` + vapp_name = "TODO" # Уникальное в рамках организации + vdc_uid = "TODO" # UUID Услуги `Виртуальный датацентр (vDC)` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vapp.baza.state_params["имя_ключа"] +nubes_vapp.baza.state_out["имя_ключа"] +nubes_vapp.baza.state_params_flat["имя_ключа"] +nubes_vapp.baza.state_out_flat["имя_ключа"] +nubes_vapp.baza.vault_secrets["имя_ключа"] +nubes_vapp.baza.vault_url +nubes_vapp.baza.vault_user_path +nubes_vapp.baza.vault_fields +``` diff --git a/generated/dev/docs/vapp_ops.md b/generated/dev/docs/vapp_ops.md new file mode 100644 index 0000000..5e1eba0 --- /dev/null +++ b/generated/dev/docs/vapp_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vapp_params_create.md) +- `delete` +- `resume` +- `suspend` diff --git a/generated/dev/docs/vapp_outputs.md b/generated/dev/docs/vapp_outputs.md new file mode 100644 index 0000000..0ff0f8e --- /dev/null +++ b/generated/dev/docs/vapp_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/vapp_params.md b/generated/dev/docs/vapp_params.md new file mode 100644 index 0000000..4c7addc --- /dev/null +++ b/generated/dev/docs/vapp_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Parameters + +- [Create params](vapp_params_create.md) +- [Modify params](vapp_params_modify.md) diff --git a/generated/dev/docs/vapp_params_create.md b/generated/dev/docs/vapp_params_create.md new file mode 100644 index 0000000..3b23cff --- /dev/null +++ b/generated/dev/docs/vapp_params_create.md @@ -0,0 +1,34 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
190nsxt_uiduuidК какой сети может быть подключен виртуальный каталогref_svc_id=22
191vapp_namestringИмеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин)
regex=^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$; minlength=3
623vdc_uiduuidВ каких ресурсах будет создан виртуальный каталогref_svc_id=21
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/vapp_params_modify.md b/generated/dev/docs/vapp_params_modify.md new file mode 100644 index 0000000..4fca747 --- /dev/null +++ b/generated/dev/docs/vapp_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/vc_nsxt.md b/generated/dev/docs/vc_nsxt.md new file mode 100644 index 0000000..0c8056d --- /dev/null +++ b/generated/dev/docs/vc_nsxt.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## MAN + +
+# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

+
diff --git a/generated/dev/docs/vc_nsxt_example.md b/generated/dev/docs/vc_nsxt_example.md new file mode 100644 index 0000000..7c5d4c4 --- /dev/null +++ b/generated/dev/docs/vc_nsxt_example.md @@ -0,0 +1,47 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Copy-ready manifest (`vc_nsxt_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_nsxt" "baza" { + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_enable_avi = false # Включение Load Balancer + virtual_services_count = 1 # Кол-во виртуальных сервисов, которые **резервируются** на AVI + vdc_type = "vdc" # Тип родительской услуги +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_nsxt.baza.state_params["имя_ключа"] +nubes_vc_nsxt.baza.state_out["имя_ключа"] +nubes_vc_nsxt.baza.state_params_flat["имя_ключа"] +nubes_vc_nsxt.baza.state_out_flat["имя_ключа"] +nubes_vc_nsxt.baza.vault_secrets["имя_ключа"] +nubes_vc_nsxt.baza.vault_url +nubes_vc_nsxt.baza.vault_user_path +nubes_vc_nsxt.baza.vault_fields +``` diff --git a/generated/dev/docs/vc_nsxt_ops.md b/generated/dev/docs/vc_nsxt_ops.md new file mode 100644 index 0000000..03af84f --- /dev/null +++ b/generated/dev/docs/vc_nsxt_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_nsxt_params_create.md) +- `delete` — Невозможно удалить если routed-сеть в Edge связана с: - vApp - VM - Kubernetes-кластер Штурвал Запустить операцию возможно, но будет ошибка +- `modify` — Изменение кластера. Параметры: [Modify params](vc_nsxt_params_modify.md) +- `reconcile` diff --git a/generated/dev/docs/vc_nsxt_outputs.md b/generated/dev/docs/vc_nsxt_outputs.md new file mode 100644 index 0000000..717cbc4 --- /dev/null +++ b/generated/dev/docs/vc_nsxt_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/vc_nsxt_params.md b/generated/dev/docs/vc_nsxt_params.md new file mode 100644 index 0000000..2cf1cc1 --- /dev/null +++ b/generated/dev/docs/vc_nsxt_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Parameters + +- [Create params](vc_nsxt_params_create.md) +- [Modify params](vc_nsxt_params_modify.md) diff --git a/generated/dev/docs/vc_nsxt_params_create.md b/generated/dev/docs/vc_nsxt_params_create.md new file mode 100644 index 0000000..9f77ac0 --- /dev/null +++ b/generated/dev/docs/vc_nsxt_params_create.md @@ -0,0 +1,27 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
8vdc_uidstringНеобходимо, если выбран тип подключаемой VDC: `vdc`ref_svc_id=21
622vdc_group_uidstringНеобходимо, если выбран тип подключаемой VDC: `groupvdc`ref_svc_id=29
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
340need_enable_avibooleanfalseПараметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указатьvalue_list=false, true
341virtual_services_countinteger1Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB`
minvalue=1; maxvalue=4
621vdc_typestringvdcПри выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc
value_list=vdc, vdcGroup
diff --git a/generated/dev/docs/vc_nsxt_params_modify.md b/generated/dev/docs/vc_nsxt_params_modify.md new file mode 100644 index 0000000..0a1fc0e --- /dev/null +++ b/generated/dev/docs/vc_nsxt_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
368need_enable_avibooleanПараметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указатьvalue_list=false, true
369virtual_services_countintegerВо избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB`
minvalue=1; maxvalue=4
371need_external_address_snatbooleanПараметр `ip Space для VIP` станет обязательным, если указан `true`value_list=false, true
372ip_space_namestringНеобходимо указывать, если включён параметр `Выделить VIP для SNAT`
diff --git a/generated/dev/docs/vc_org.md b/generated/dev/docs/vc_org.md new file mode 100644 index 0000000..e500201 --- /dev/null +++ b/generated/dev/docs/vc_org.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## MAN + +
+# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak. +
diff --git a/generated/dev/docs/vc_org_example.md b/generated/dev/docs/vc_org_example.md new file mode 100644 index 0000000..c6e548e --- /dev/null +++ b/generated/dev/docs/vc_org_example.md @@ -0,0 +1,46 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Copy-ready manifest (`vc_org_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_org" "baza" { + resource_realm = "TODO" # Облако в котором будет развёрнут тенант + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + organization_type = "iaas" # Доступ к организации предоставляется только при выборе `iaas` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_org.baza.state_params["имя_ключа"] +nubes_vc_org.baza.state_out["имя_ключа"] +nubes_vc_org.baza.state_params_flat["имя_ключа"] +nubes_vc_org.baza.state_out_flat["имя_ключа"] +nubes_vc_org.baza.vault_secrets["имя_ключа"] +nubes_vc_org.baza.vault_url +nubes_vc_org.baza.vault_user_path +nubes_vc_org.baza.vault_fields +``` diff --git a/generated/dev/docs/vc_org_ops.md b/generated/dev/docs/vc_org_ops.md new file mode 100644 index 0000000..a0c2067 --- /dev/null +++ b/generated/dev/docs/vc_org_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_org_params_create.md) +- `delete` — Удалить возможно только через 14 дней после выполнения операции `suspend` **ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта** Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов +- `modify` — Изменение кластера. Параметры: [Modify params](vc_org_params_modify.md) +- `reconcile` +- `resume` +- `suspend` — Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта diff --git a/generated/dev/docs/vc_org_outputs.md b/generated/dev/docs/vc_org_outputs.md new file mode 100644 index 0000000..9de11b9 --- /dev/null +++ b/generated/dev/docs/vc_org_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["urlConnect"]` + diff --git a/generated/dev/docs/vc_org_params.md b/generated/dev/docs/vc_org_params.md new file mode 100644 index 0000000..e187b07 --- /dev/null +++ b/generated/dev/docs/vc_org_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Parameters + +- [Create params](vc_org_params_create.md) +- [Modify params](vc_org_params_modify.md) diff --git a/generated/dev/docs/vc_org_params_create.md b/generated/dev/docs/vc_org_params_create.md new file mode 100644 index 0000000..28f12f0 --- /dev/null +++ b/generated/dev/docs/vc_org_params_create.md @@ -0,0 +1,36 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
418resource_realmstringВыбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы
func=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
556organization_typestringiaasДоступ к организации предоставляется только при выборе `iaas`value_list=iaas, saas
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/vc_org_params_modify.md b/generated/dev/docs/vc_org_params_modify.md new file mode 100644 index 0000000..4142f72 --- /dev/null +++ b/generated/dev/docs/vc_org_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
662v_ipconfigurearray-map-fixed
diff --git a/generated/dev/docs/vc_vdc.md b/generated/dev/docs/vc_vdc.md new file mode 100644 index 0000000..2d8fa57 --- /dev/null +++ b/generated/dev/docs/vc_vdc.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## MAN + +
+# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
+
diff --git a/generated/dev/docs/vc_vdc_example.md b/generated/dev/docs/vc_vdc_example.md new file mode 100644 index 0000000..0933efa --- /dev/null +++ b/generated/dev/docs/vc_vdc_example.md @@ -0,0 +1,51 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Copy-ready manifest (`vc_vdc_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vdc" "baza" { + organization_uid = "TODO" # Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` + provider_vdc = "TODO" # Приписка `2.8` - Скорость vCPU - 2800MHz И так далее... + network_provider = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + storage_config = [{}] # Первая политика в списке указывается как "По умолчанию" + cpu_guaranteed = 20 # % резервирования + cpu_allocated = 10 # шт + mem_allocated = 20 # gb +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vdc.baza.state_params["имя_ключа"] +nubes_vc_vdc.baza.state_out["имя_ключа"] +nubes_vc_vdc.baza.state_params_flat["имя_ключа"] +nubes_vc_vdc.baza.state_out_flat["имя_ключа"] +nubes_vc_vdc.baza.vault_secrets["имя_ключа"] +nubes_vc_vdc.baza.vault_url +nubes_vc_vdc.baza.vault_user_path +nubes_vc_vdc.baza.vault_fields +``` diff --git a/generated/dev/docs/vc_vdc_group.md b/generated/dev/docs/vc_vdc_group.md new file mode 100644 index 0000000..1ff3cd6 --- /dev/null +++ b/generated/dev/docs/vc_vdc_group.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## MAN + +
+# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc` +
diff --git a/generated/dev/docs/vc_vdc_group_example.md b/generated/dev/docs/vc_vdc_group_example.md new file mode 100644 index 0000000..a2f1480 --- /dev/null +++ b/generated/dev/docs/vc_vdc_group_example.md @@ -0,0 +1,43 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Copy-ready manifest (`vc_vdc_group_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vdc_group" "baza" { + vdc_uid = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vdc_group.baza.state_params["имя_ключа"] +nubes_vc_vdc_group.baza.state_out["имя_ключа"] +nubes_vc_vdc_group.baza.state_params_flat["имя_ключа"] +nubes_vc_vdc_group.baza.state_out_flat["имя_ключа"] +nubes_vc_vdc_group.baza.vault_secrets["имя_ключа"] +nubes_vc_vdc_group.baza.vault_url +nubes_vc_vdc_group.baza.vault_user_path +nubes_vc_vdc_group.baza.vault_fields +``` diff --git a/generated/dev/docs/vc_vdc_group_ops.md b/generated/dev/docs/vc_vdc_group_ops.md new file mode 100644 index 0000000..9552c15 --- /dev/null +++ b/generated/dev/docs/vc_vdc_group_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Operations + +- `add_vdc` — **Не нужно** выбирать UUID VDC, который уже был добавлен в группу +- `create` — Создание кластера. Параметры: [Create params](vc_vdc_group_params_create.md) +- `delete` +- `reconcile` +- `remove_vdc` diff --git a/generated/dev/docs/vc_vdc_group_outputs.md b/generated/dev/docs/vc_vdc_group_outputs.md new file mode 100644 index 0000000..5714cde --- /dev/null +++ b/generated/dev/docs/vc_vdc_group_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/vc_vdc_group_params.md b/generated/dev/docs/vc_vdc_group_params.md new file mode 100644 index 0000000..d292ab9 --- /dev/null +++ b/generated/dev/docs/vc_vdc_group_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Parameters + +- [Create params](vc_vdc_group_params_create.md) +- [Modify params](vc_vdc_group_params_modify.md) diff --git a/generated/dev/docs/vc_vdc_group_params_create.md b/generated/dev/docs/vc_vdc_group_params_create.md new file mode 100644 index 0000000..624e982 --- /dev/null +++ b/generated/dev/docs/vc_vdc_group_params_create.md @@ -0,0 +1,20 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
618vdc_uiduuidref_svc_id=21
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/dev/docs/vc_vdc_group_params_modify.md b/generated/dev/docs/vc_vdc_group_params_modify.md new file mode 100644 index 0000000..721de57 --- /dev/null +++ b/generated/dev/docs/vc_vdc_group_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Modify params + +None. diff --git a/generated/dev/docs/vc_vdc_ops.md b/generated/dev/docs/vc_vdc_ops.md new file mode 100644 index 0000000..b1a1776 --- /dev/null +++ b/generated/dev/docs/vc_vdc_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vdc_params_create.md) +- `delete` — Удалить возможно только через 14 дней после выполнения операции `suspend` **ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC** Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vdc_params_modify.md) +- `reconcile` +- `resume` +- `suspend` — Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC diff --git a/generated/dev/docs/vc_vdc_outputs.md b/generated/dev/docs/vc_vdc_outputs.md new file mode 100644 index 0000000..a435292 --- /dev/null +++ b/generated/dev/docs/vc_vdc_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["vdcName"]` + diff --git a/generated/dev/docs/vc_vdc_params.md b/generated/dev/docs/vc_vdc_params.md new file mode 100644 index 0000000..03c0037 --- /dev/null +++ b/generated/dev/docs/vc_vdc_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Parameters + +- [Create params](vc_vdc_params_create.md) +- [Modify params](vc_vdc_params_modify.md) diff --git a/generated/dev/docs/vc_vdc_params_create.md b/generated/dev/docs/vc_vdc_params_create.md new file mode 100644 index 0000000..01bc413 --- /dev/null +++ b/generated/dev/docs/vc_vdc_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
30organization_uiduuidДля создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director`ref_svc_id=19
335provider_vdcstringПриписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
746network_providerstring
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
361storage_configarray-map-fixed[{}]Первая политика в списке указывается как &quot;По умолчанию&quot;
397cpu_guaranteedinteger20Свойство нельзя изменить после созданияvalue_list=20, 50, 80
557cpu_allocatedinteger10штminvalue=1; maxvalue=200
558mem_allocatedinteger20gbminvalue=1; maxvalue=4096
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/vc_vdc_params_modify.md b/generated/dev/docs/vc_vdc_params_modify.md new file mode 100644 index 0000000..4bb0906 --- /dev/null +++ b/generated/dev/docs/vc_vdc_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
560cpu_allocatedintegerштminvalue=1; maxvalue=999
561mem_allocatedintegergbminvalue=1; maxvalue=1024
562storage_configarray-map-fixedПервая политика в списке указывается как &quot;По умолчанию&quot;
diff --git a/generated/dev/docs/vc_vm_v3.md b/generated/dev/docs/vc_vm_v3.md new file mode 100644 index 0000000..95856d8 --- /dev/null +++ b/generated/dev/docs/vc_vm_v3.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## MAN + +
+# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики +
diff --git a/generated/dev/docs/vc_vm_v3_example.md b/generated/dev/docs/vc_vm_v3_example.md new file mode 100644 index 0000000..e6d39ac --- /dev/null +++ b/generated/dev/docs/vc_vm_v3_example.md @@ -0,0 +1,53 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Copy-ready manifest (`vc_vm_v3_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vm_v3" "baza" { + vapp_uid = "TODO" + ip_space_name = "TODO" + image_vm = "TODO" + user_public_key = "TODO" # Публичная часть SSH-Ключа для пользователя + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + vm_name = "web01" + vm_cpu = 1 # шт + vm_ram = 1 # Gb + user_login = "myuser" # Учётка SSH + access_port_list = ["22"] # По каким портам открыть доступ к ВМ. Порты 22, 10050 проставляются автоматически Пока доступно: 22 - SSH, 80 - HTTP, 443 - HTTPS, 10050 - ZABBIX +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vm_v3.baza.state_params["имя_ключа"] +nubes_vc_vm_v3.baza.state_out["имя_ключа"] +nubes_vc_vm_v3.baza.state_params_flat["имя_ключа"] +nubes_vc_vm_v3.baza.state_out_flat["имя_ключа"] +nubes_vc_vm_v3.baza.vault_secrets["имя_ключа"] +nubes_vc_vm_v3.baza.vault_url +nubes_vc_vm_v3.baza.vault_user_path +nubes_vc_vm_v3.baza.vault_fields +``` diff --git a/generated/dev/docs/vc_vm_v3_ops.md b/generated/dev/docs/vc_vm_v3_ops.md new file mode 100644 index 0000000..29b88fb --- /dev/null +++ b/generated/dev/docs/vc_vm_v3_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vm_v3_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vm_v3_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/dev/docs/vc_vm_v3_outputs.md b/generated/dev/docs/vc_vm_v3_outputs.md new file mode 100644 index 0000000..d0ef2e1 --- /dev/null +++ b/generated/dev/docs/vc_vm_v3_outputs.md @@ -0,0 +1,30 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["fqdn"]` +- `state_out_flat["internalConnect"]` + diff --git a/generated/dev/docs/vc_vm_v3_params.md b/generated/dev/docs/vc_vm_v3_params.md new file mode 100644 index 0000000..9c63ad1 --- /dev/null +++ b/generated/dev/docs/vc_vm_v3_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Parameters + +- [Create params](vc_vm_v3_params_create.md) +- [Modify params](vc_vm_v3_params_modify.md) diff --git a/generated/dev/docs/vc_vm_v3_params_create.md b/generated/dev/docs/vc_vm_v3_params_create.md new file mode 100644 index 0000000..aff77f9 --- /dev/null +++ b/generated/dev/docs/vc_vm_v3_params_create.md @@ -0,0 +1,46 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
407vapp_uiduuidref_svc_id=26
411vm_diskintegerОсновной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму
regex=^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$
412ip_space_namestring
413access_ip_listjsonФормат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено [&quot;0.0.0.0/0&quot;]
regex=^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$
414image_vmstring
415cloud_inityamlМожно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов.
417user_public_keystringПубличная часть SSH-Ключа для пользователя
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
408vm_namestringweb01regex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
409vm_cpuinteger1штminvalue=1; maxvalue=64
410vm_raminteger1Gbminvalue=1; maxvalue=256
416user_loginmyuserУчётка SSH
448access_port_liststring["22"]По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/dev/docs/vc_vm_v3_params_modify.md b/generated/dev/docs/vc_vm_v3_params_modify.md new file mode 100644 index 0000000..e993928 --- /dev/null +++ b/generated/dev/docs/vc_vm_v3_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
493vm_cpuintegerштminvalue=1; maxvalue=64
494vm_ramintegerGbminvalue=1; maxvalue=256
495vm_diskintegerОсновной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму
regex=^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$
496ip_space_namestringno-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev
value_list=no-needed, internet-antiddos-v1, internet-no-antiddos-v1, internet-ipv4-v1
497access_ip_listjsonФормат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено [&quot;0.0.0.0/0&quot;]
498access_port_listjsonФормат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX
diff --git a/generated/dev/docs/vcexternalip.md b/generated/dev/docs/vcexternalip.md new file mode 100644 index 0000000..3fe625c --- /dev/null +++ b/generated/dev/docs/vcexternalip.md @@ -0,0 +1,13 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## MAN + +
+**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса. +
diff --git a/generated/dev/docs/vcexternalip_example.md b/generated/dev/docs/vcexternalip_example.md new file mode 100644 index 0000000..fbdb48b --- /dev/null +++ b/generated/dev/docs/vcexternalip_example.md @@ -0,0 +1,51 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Copy-ready manifest (`vcexternalip_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vcexternalip" "baza" { + service_uid = "TODO" + internal_addr_access = "TODO" # Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true + from_service_namespace = "TODO" # Namespace составной услуги (для отображения правил FW) + ip_space_name = "TODO" # Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + dnat_create = true # Необходимо создать DNAT-правило. + internal_port_access = 5432 # Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vcexternalip.baza.state_params["имя_ключа"] +nubes_vcexternalip.baza.state_out["имя_ключа"] +nubes_vcexternalip.baza.state_params_flat["имя_ключа"] +nubes_vcexternalip.baza.state_out_flat["имя_ключа"] +nubes_vcexternalip.baza.vault_secrets["имя_ключа"] +nubes_vcexternalip.baza.vault_url +nubes_vcexternalip.baza.vault_user_path +nubes_vcexternalip.baza.vault_fields +``` diff --git a/generated/dev/docs/vcexternalip_ops.md b/generated/dev/docs/vcexternalip_ops.md new file mode 100644 index 0000000..5668481 --- /dev/null +++ b/generated/dev/docs/vcexternalip_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vcexternalip_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vcexternalip_params_modify.md) diff --git a/generated/dev/docs/vcexternalip_outputs.md b/generated/dev/docs/vcexternalip_outputs.md new file mode 100644 index 0000000..aaa59e1 --- /dev/null +++ b/generated/dev/docs/vcexternalip_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalIp"]` + diff --git a/generated/dev/docs/vcexternalip_params.md b/generated/dev/docs/vcexternalip_params.md new file mode 100644 index 0000000..1db2fcc --- /dev/null +++ b/generated/dev/docs/vcexternalip_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Parameters + +- [Create params](vcexternalip_params_create.md) +- [Modify params](vcexternalip_params_modify.md) diff --git a/generated/dev/docs/vcexternalip_params_create.md b/generated/dev/docs/vcexternalip_params_create.md new file mode 100644 index 0000000..995a3b8 --- /dev/null +++ b/generated/dev/docs/vcexternalip_params_create.md @@ -0,0 +1,36 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
140service_uidstring
143snat_createbooleanНеобходимо создать SNAT-правило.
144internal_addr_accessstringВнутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true
324from_service_namespacestringNamespace составной услуги (для отображения правил FW)
325from_service_cloud_edge_namestringEdge составной услуги (указывать, если resourceRealm связан с CloudDirector)
326from_service_cloud_vdc_nameVdc составной услуги (указывать, если resourceRealm связан с CloudDirector)
327from_service_cloud_org_namestringOrg составной услуги (указывать, если resourceRealm связан с CloudDirector)
328from_service_cloud_vmware_urlstringApi-Url составной услуги (указывать, если resourceRealm связан с CloudDirector)
336ip_space_namestringТип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)
488resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
624from_service_cloud_edge_scopestring
626from_service_vdc_group_namestring
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
141dnat_createbooleantrueНеобходимо создать DNAT-правило.
142internal_port_accessstring5432Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
diff --git a/generated/dev/docs/vcexternalip_params_modify.md b/generated/dev/docs/vcexternalip_params_modify.md new file mode 100644 index 0000000..f17b66c --- /dev/null +++ b/generated/dev/docs/vcexternalip_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
627service_uidstring
628ip_space_namestring
629dnat_createboolean
630snat_createboolean
631internal_port_accessstring
632internal_addr_accessstring
diff --git a/generated/dev/docs/zones_v2.md b/generated/dev/docs/zones_v2.md new file mode 100644 index 0000000..187c13e --- /dev/null +++ b/generated/dev/docs/zones_v2.md @@ -0,0 +1,13 @@ +# Resource nubes_zones_v2 + +Service ID: `109` + +Service Name: Управление днс-зонами и записями + +[Manual](zones_v2.md) | [Create params](zones_v2_params_create.md) | [Modify params](zones_v2_params_modify.md) | [Output params](zones_v2_outputs.md) | [Operations](zones_v2_ops.md) | [Example](zones_v2_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/dev/docs/zones_v2_example.md b/generated/dev/docs/zones_v2_example.md new file mode 100644 index 0000000..4d130a4 --- /dev/null +++ b/generated/dev/docs/zones_v2_example.md @@ -0,0 +1,51 @@ +# Resource nubes_zones_v2 + +Service ID: `109` + +Service Name: Управление днс-зонами и записями + +[Manual](zones_v2.md) | [Create params](zones_v2_params_create.md) | [Modify params](zones_v2_params_modify.md) | [Output params](zones_v2_outputs.md) | [Operations](zones_v2_ops.md) | [Example](zones_v2_example.md) + +## Copy-ready manifest (`zones_v2_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.39" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_zones_v2" "baza" { + zone_name = "TODO" # Имя зоны + resource_realm = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + zone_kind = "NATIVE" # Тип зоны + zone_time_to_refresh = 86400 # Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов + zone_time_to_retry = 7200 # Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов + zone_time_to_expire = 3600000 # Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным + zone_time_to_live = 86400 # Время, в течение которого информация будет кешироваться другими DNS-серверами +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_zones_v2.baza.state_params["имя_ключа"] +nubes_zones_v2.baza.state_out["имя_ключа"] +nubes_zones_v2.baza.state_params_flat["имя_ключа"] +nubes_zones_v2.baza.state_out_flat["имя_ключа"] +nubes_zones_v2.baza.vault_secrets["имя_ключа"] +nubes_zones_v2.baza.vault_url +nubes_zones_v2.baza.vault_user_path +nubes_zones_v2.baza.vault_fields +``` diff --git a/generated/dev/docs/zones_v2_ops.md b/generated/dev/docs/zones_v2_ops.md new file mode 100644 index 0000000..dd3e0ab --- /dev/null +++ b/generated/dev/docs/zones_v2_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_zones_v2 + +Service ID: `109` + +Service Name: Управление днс-зонами и записями + +[Manual](zones_v2.md) | [Create params](zones_v2_params_create.md) | [Modify params](zones_v2_params_modify.md) | [Output params](zones_v2_outputs.md) | [Operations](zones_v2_ops.md) | [Example](zones_v2_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](zones_v2_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](zones_v2_params_modify.md) diff --git a/generated/dev/docs/zones_v2_outputs.md b/generated/dev/docs/zones_v2_outputs.md new file mode 100644 index 0000000..7d38f81 --- /dev/null +++ b/generated/dev/docs/zones_v2_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_zones_v2 + +Service ID: `109` + +Service Name: Управление днс-зонами и записями + +[Manual](zones_v2.md) | [Create params](zones_v2_params_create.md) | [Modify params](zones_v2_params_modify.md) | [Output params](zones_v2_outputs.md) | [Operations](zones_v2_ops.md) | [Example](zones_v2_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/dev/docs/zones_v2_params.md b/generated/dev/docs/zones_v2_params.md new file mode 100644 index 0000000..fa69049 --- /dev/null +++ b/generated/dev/docs/zones_v2_params.md @@ -0,0 +1,12 @@ +# Resource nubes_zones_v2 + +Service ID: `109` + +Service Name: Управление днс-зонами и записями + +[Manual](zones_v2.md) | [Create params](zones_v2_params_create.md) | [Modify params](zones_v2_params_modify.md) | [Output params](zones_v2_outputs.md) | [Operations](zones_v2_ops.md) | [Example](zones_v2_example.md) + +## Parameters + +- [Create params](zones_v2_params_create.md) +- [Modify params](zones_v2_params_modify.md) diff --git a/generated/dev/docs/zones_v2_params_create.md b/generated/dev/docs/zones_v2_params_create.md new file mode 100644 index 0000000..98dab0e --- /dev/null +++ b/generated/dev/docs/zones_v2_params_create.md @@ -0,0 +1,30 @@ +# Resource nubes_zones_v2 + +Service ID: `109` + +Service Name: Управление днс-зонами и записями + +[Manual](zones_v2.md) | [Create params](zones_v2_params_create.md) | [Modify params](zones_v2_params_modify.md) | [Output params](zones_v2_outputs.md) | [Operations](zones_v2_ops.md) | [Example](zones_v2_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
761zone_namestringИмя зоныregex=^([a-z0-9-]+\.)+[a-z0-9-]+$; maxlength=64; minlength=1
763zone_masters_for_slavestringПример:
**8.8.8.8 8.8.4.4**
768resource_realmstringfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
762zone_kindstringNATIVEТип зоныvalue_list=NATIVE, SLAVE
764zone_time_to_refreshinteger86400Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверовminvalue=600
765zone_time_to_retryinteger7200Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверовminvalue=600
766zone_time_to_expireinteger3600000Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичнымminvalue=3600
767zone_time_to_liveinteger86400Время, в течение которого информация будет кешироваться другими DNS-серверамиminvalue=600
diff --git a/generated/dev/docs/zones_v2_params_modify.md b/generated/dev/docs/zones_v2_params_modify.md new file mode 100644 index 0000000..4987357 --- /dev/null +++ b/generated/dev/docs/zones_v2_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_zones_v2 + +Service ID: `109` + +Service Name: Управление днс-зонами и записями + +[Manual](zones_v2.md) | [Create params](zones_v2_params_create.md) | [Modify params](zones_v2_params_modify.md) | [Output params](zones_v2_outputs.md) | [Operations](zones_v2_ops.md) | [Example](zones_v2_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
769recordsarray-map-fixed
diff --git a/generated/dev/go/.gitkeep b/generated/dev/go/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/dev/go/109_zones_v2_resource.go b/generated/dev/go/109_zones_v2_resource.go new file mode 100644 index 0000000..6328798 --- /dev/null +++ b/generated/dev/go/109_zones_v2_resource.go @@ -0,0 +1,467 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: zones_v2 +// Service ID: 109 + +var _ resource.Resource = &ZonesV2Resource{} +var _ resource.ResourceWithModifyPlan = &ZonesV2Resource{} +var _ resource.ResourceWithImportState = &ZonesV2Resource{} + +type ZonesV2Resource struct { + client *core.UniversalClient +} + +type ZonesV2Model struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Records types.String `tfsdk:"records"` + ResourceRealm types.String `tfsdk:"resource_realm"` + ZoneKind types.String `tfsdk:"zone_kind"` + ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` + ZoneName types.String `tfsdk:"zone_name"` + ZoneTimeToExpire types.Int64 `tfsdk:"zone_time_to_expire"` + ZoneTimeToLive types.Int64 `tfsdk:"zone_time_to_live"` + ZoneTimeToRefresh types.Int64 `tfsdk:"zone_time_to_refresh"` + ZoneTimeToRetry types.Int64 `tfsdk:"zone_time_to_retry"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewZonesV2Resource() resource.Resource { + return &ZonesV2Resource{} +} + +func (r *ZonesV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_zones_v2" +} + +func (r *ZonesV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "records": schema.StringAttribute{Required: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, + "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, + "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, + "zone_time_to_expire": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600000), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, + "zone_time_to_live": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, + "zone_time_to_refresh": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, + "zone_time_to_retry": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7200), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ZonesV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *ZonesV2Model + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *ZonesV2Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan ZonesV2Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { + if plan.ZoneKind.ValueString() != state.ZoneKind.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { + if plan.ZoneMastersForSlave.ValueString() != state.ZoneMastersForSlave.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { + if plan.ZoneName.ValueString() != state.ZoneName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToExpire.IsNull() && !plan.ZoneTimeToExpire.IsUnknown() && !state.ZoneTimeToExpire.IsNull() && !state.ZoneTimeToExpire.IsUnknown() { + if plan.ZoneTimeToExpire.ValueInt64() != state.ZoneTimeToExpire.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_expire", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToLive.IsNull() && !plan.ZoneTimeToLive.IsUnknown() && !state.ZoneTimeToLive.IsNull() && !state.ZoneTimeToLive.IsUnknown() { + if plan.ZoneTimeToLive.ValueInt64() != state.ZoneTimeToLive.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_live", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToRefresh.IsNull() && !plan.ZoneTimeToRefresh.IsUnknown() && !state.ZoneTimeToRefresh.IsNull() && !state.ZoneTimeToRefresh.IsUnknown() { + if plan.ZoneTimeToRefresh.ValueInt64() != state.ZoneTimeToRefresh.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_refresh", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToRetry.IsNull() && !plan.ZoneTimeToRetry.IsUnknown() && !state.ZoneTimeToRetry.IsNull() && !state.ZoneTimeToRetry.IsUnknown() { + if plan.ZoneTimeToRetry.ValueInt64() != state.ZoneTimeToRetry.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_retry", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Records.IsNull() || config.Records.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "records is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ZoneName.IsNull() || config.ZoneName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 761: resources_core.FormatString(config.ZoneName), + 762: resources_core.FormatString(config.ZoneKind), + 763: resources_core.FormatString(config.ZoneMastersForSlave), + 764: resources_core.FormatInt64(config.ZoneTimeToRefresh), + 765: resources_core.FormatInt64(config.ZoneTimeToRetry), + 766: resources_core.FormatInt64(config.ZoneTimeToExpire), + 767: resources_core.FormatInt64(config.ZoneTimeToLive), + 768: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 109, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *ZonesV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data ZonesV2Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Records.IsNull() || data.Records.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "records is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 761: resources_core.FormatString(data.ZoneName), + 762: resources_core.FormatString(data.ZoneKind), + 763: resources_core.FormatString(data.ZoneMastersForSlave), + 764: resources_core.FormatInt64(data.ZoneTimeToRefresh), + 765: resources_core.FormatInt64(data.ZoneTimeToRetry), + 766: resources_core.FormatInt64(data.ZoneTimeToExpire), + 767: resources_core.FormatInt64(data.ZoneTimeToLive), + 768: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 109, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "records", Field: "Records", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "int64"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "int64"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "int64"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ZonesV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ZonesV2Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 109, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "records", Field: "Records", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "int64"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "int64"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "int64"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *ZonesV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ZonesV2Model + var state ZonesV2Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.Records.IsNull() != state.Records.IsNull() || plan.Records.IsUnknown() != state.Records.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.Records.IsNull() && !plan.Records.IsUnknown() && !state.Records.IsNull() && !state.Records.IsUnknown() { + if plan.Records.ValueString() != state.Records.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 769: resources_core.FormatString(plan.Records), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 109, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "records", Field: "Records", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "int64"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "int64"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "int64"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ZonesV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ZonesV2Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *ZonesV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *ZonesV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/110_dnszone_resource.go b/generated/dev/go/110_dnszone_resource.go new file mode 100644 index 0000000..fe1cff6 --- /dev/null +++ b/generated/dev/go/110_dnszone_resource.go @@ -0,0 +1,463 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: dnszone +// Service ID: 110 + +var _ resource.Resource = &DnszoneResource{} +var _ resource.ResourceWithModifyPlan = &DnszoneResource{} +var _ resource.ResourceWithImportState = &DnszoneResource{} + +type DnszoneResource struct { + client *core.UniversalClient +} + +type DnszoneModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + ResourceRealm types.String `tfsdk:"resource_realm"` + ZoneEmail types.String `tfsdk:"zone_email"` + ZoneKind types.String `tfsdk:"zone_kind"` + ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` + ZoneName types.String `tfsdk:"zone_name"` + ZoneTimeToExpire types.String `tfsdk:"zone_time_to_expire"` + ZoneTimeToLive types.String `tfsdk:"zone_time_to_live"` + ZoneTimeToRefresh types.String `tfsdk:"zone_time_to_refresh"` + ZoneTimeToRetry types.String `tfsdk:"zone_time_to_retry"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewDnszoneResource() resource.Resource { + return &DnszoneResource{} +} + +func (r *DnszoneResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dnszone" +} + +func (r *DnszoneResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "zone_email": schema.StringAttribute{Required: true, MarkdownDescription: "Контактный e-mail Не изменяется после создания."}, + "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, + "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, + "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, + "zone_time_to_expire": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3600000"), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, + "zone_time_to_live": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, + "zone_time_to_refresh": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, + "zone_time_to_retry": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("7200"), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *DnszoneResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *DnszoneModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *DnszoneModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan DnszoneModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneEmail.IsNull() && !plan.ZoneEmail.IsUnknown() && !state.ZoneEmail.IsNull() && !state.ZoneEmail.IsUnknown() { + if plan.ZoneEmail.ValueString() != state.ZoneEmail.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_email", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { + if plan.ZoneKind.ValueString() != state.ZoneKind.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { + if plan.ZoneMastersForSlave.ValueString() != state.ZoneMastersForSlave.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { + if plan.ZoneName.ValueString() != state.ZoneName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToExpire.IsNull() && !plan.ZoneTimeToExpire.IsUnknown() && !state.ZoneTimeToExpire.IsNull() && !state.ZoneTimeToExpire.IsUnknown() { + if plan.ZoneTimeToExpire.ValueString() != state.ZoneTimeToExpire.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_expire", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToLive.IsNull() && !plan.ZoneTimeToLive.IsUnknown() && !state.ZoneTimeToLive.IsNull() && !state.ZoneTimeToLive.IsUnknown() { + if plan.ZoneTimeToLive.ValueString() != state.ZoneTimeToLive.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_live", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToRefresh.IsNull() && !plan.ZoneTimeToRefresh.IsUnknown() && !state.ZoneTimeToRefresh.IsNull() && !state.ZoneTimeToRefresh.IsUnknown() { + if plan.ZoneTimeToRefresh.ValueString() != state.ZoneTimeToRefresh.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_refresh", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToRetry.IsNull() && !plan.ZoneTimeToRetry.IsUnknown() && !state.ZoneTimeToRetry.IsNull() && !state.ZoneTimeToRetry.IsUnknown() { + if plan.ZoneTimeToRetry.ValueString() != state.ZoneTimeToRetry.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_retry", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ZoneEmail.IsNull() || config.ZoneEmail.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") + return + } + if config.ZoneName.IsNull() || config.ZoneName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 66: resources_core.FormatString(config.ZoneEmail), + 67: resources_core.FormatString(config.ZoneName), + 68: resources_core.FormatString(config.ZoneKind), + 69: resources_core.FormatString(config.ZoneMastersForSlave), + 70: resources_core.FormatString(config.ZoneTimeToRefresh), + 71: resources_core.FormatString(config.ZoneTimeToRetry), + 72: resources_core.FormatString(config.ZoneTimeToExpire), + 73: resources_core.FormatString(config.ZoneTimeToLive), + 284: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 110, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *DnszoneResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data DnszoneModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.ZoneEmail.IsNull() || data.ZoneEmail.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") + return + } + if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 66: resources_core.FormatString(data.ZoneEmail), + 67: resources_core.FormatString(data.ZoneName), + 68: resources_core.FormatString(data.ZoneKind), + 69: resources_core.FormatString(data.ZoneMastersForSlave), + 70: resources_core.FormatString(data.ZoneTimeToRefresh), + 71: resources_core.FormatString(data.ZoneTimeToRetry), + 72: resources_core.FormatString(data.ZoneTimeToExpire), + 73: resources_core.FormatString(data.ZoneTimeToLive), + 284: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 110, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnszoneResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DnszoneModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 110, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan DnszoneModel + var state DnszoneModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 110, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnszoneResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state DnszoneModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *DnszoneResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *DnszoneResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/111_dnsrecord_resource.go b/generated/dev/go/111_dnsrecord_resource.go new file mode 100644 index 0000000..89c2a20 --- /dev/null +++ b/generated/dev/go/111_dnsrecord_resource.go @@ -0,0 +1,482 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: dnsrecord +// Service ID: 111 + +var _ resource.Resource = &DnsrecordResource{} +var _ resource.ResourceWithModifyPlan = &DnsrecordResource{} +var _ resource.ResourceWithImportState = &DnsrecordResource{} + +type DnsrecordResource struct { + client *core.UniversalClient +} + +type DnsrecordModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + RecordInput types.String `tfsdk:"record_input"` + RecordName types.String `tfsdk:"record_name"` + RecordTTL types.Int64 `tfsdk:"record_t_t_l"` + RecordType types.String `tfsdk:"record_type"` + ZoneUid types.String `tfsdk:"zone_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewDnsrecordResource() resource.Resource { + return &DnsrecordResource{} +} + +func (r *DnsrecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dnsrecord" +} + +func (r *DnsrecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP"}, + "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя записи. Имя зоны в конце проставляется автоматически Не изменяется после создания."}, + "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена Не изменяется после создания."}, + "record_type": schema.StringAttribute{Required: true, MarkdownDescription: "Тип DNS-записи Не изменяется после создания."}, + "zone_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID экземпляра услуги DNS-зоны Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *DnsrecordResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *DnsrecordModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *DnsrecordModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan DnsrecordModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, plan.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != plan.ZoneUid.ValueString() { + plan.ZoneUid = types.StringValue(resolvedZoneUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.RecordName.IsNull() && !plan.RecordName.IsUnknown() && !state.RecordName.IsNull() && !state.RecordName.IsUnknown() { + if plan.RecordName.ValueString() != state.RecordName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить record_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RecordTTL.IsNull() && !plan.RecordTTL.IsUnknown() && !state.RecordTTL.IsNull() && !state.RecordTTL.IsUnknown() { + if plan.RecordTTL.ValueInt64() != state.RecordTTL.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить record_t_t_l", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RecordType.IsNull() && !plan.RecordType.IsUnknown() && !state.RecordType.IsNull() && !state.RecordType.IsUnknown() { + if plan.RecordType.ValueString() != state.RecordType.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить record_type", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { + if plan.ZoneUid.ValueString() != state.ZoneUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.RecordInput.IsNull() || config.RecordInput.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") + return + } + if config.RecordName.IsNull() || config.RecordName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") + return + } + if config.RecordType.IsNull() || config.RecordType.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") + return + } + if config.ZoneUid.IsNull() || config.ZoneUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.ZoneUid.IsNull() && !config.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, config.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != config.ZoneUid.ValueString() { + config.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + params := map[int]string{ + 75: resources_core.FormatString(config.ZoneUid), + 76: resources_core.FormatString(config.RecordType), + 77: resources_core.FormatString(config.RecordName), + 78: resources_core.FormatString(config.RecordInput), + 450: resources_core.FormatInt64(config.RecordTTL), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 111, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *DnsrecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data DnsrecordModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.RecordInput.IsNull() || data.RecordInput.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") + return + } + if data.RecordName.IsNull() || data.RecordName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") + return + } + if data.RecordType.IsNull() || data.RecordType.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") + return + } + if data.ZoneUid.IsNull() || data.ZoneUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") + return + } + if !data.ZoneUid.IsNull() && !data.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, data.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedZoneUid != "" && resolvedZoneUid != data.ZoneUid.ValueString() { + data.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 75: resources_core.FormatString(data.ZoneUid), + 76: resources_core.FormatString(data.RecordType), + 77: resources_core.FormatString(data.RecordName), + 78: resources_core.FormatString(data.RecordInput), + 450: resources_core.FormatInt64(data.RecordTTL), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 111, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "recordInput", Field: "RecordInput", Type: "string"}, + {Code: "recordName", Field: "RecordName", Type: "string"}, + {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, + {Code: "recordType", Field: "RecordType", Type: "string"}, + {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, state.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { + state.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnsrecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DnsrecordModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 111, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "recordInput", Field: "RecordInput", Type: "string"}, + {Code: "recordName", Field: "RecordName", Type: "string"}, + {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, + {Code: "recordType", Field: "RecordType", Type: "string"}, + {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, newState.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != newState.ZoneUid.ValueString() { + newState.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan DnsrecordModel + var state DnsrecordModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.RecordInput.IsNull() != state.RecordInput.IsNull() || plan.RecordInput.IsUnknown() != state.RecordInput.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.RecordInput.IsNull() && !plan.RecordInput.IsUnknown() && !state.RecordInput.IsNull() && !state.RecordInput.IsUnknown() { + if plan.RecordInput.ValueString() != state.RecordInput.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 256: resources_core.FormatString(plan.RecordInput), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 111, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "recordInput", Field: "RecordInput", Type: "string"}, + {Code: "recordName", Field: "RecordName", Type: "string"}, + {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, + {Code: "recordType", Field: "RecordType", Type: "string"}, + {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, state.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { + state.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnsrecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state DnsrecordModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *DnsrecordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *DnsrecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/115_mariadb_database_resource.go b/generated/dev/go/115_mariadb_database_resource.go new file mode 100644 index 0000000..d5f4642 --- /dev/null +++ b/generated/dev/go/115_mariadb_database_resource.go @@ -0,0 +1,350 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mariadb +// Subresource: database + +var _ resource.Resource = &MariadbDatabaseResource{} + +// MariadbDatabaseResource manages a subresource via operations. +type MariadbDatabaseResource struct { + client *core.UniversalClient +} + +type MariadbDatabaseModel struct { + ID types.String `tfsdk:"id"` + MariadbID types.String `tfsdk:"mariadb_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + DatabaseName types.String `tfsdk:"database_name"` +} + +func NewMariadbDatabaseResource() resource.Resource { + return &MariadbDatabaseResource{} +} + +func (r *MariadbDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mariadb_database" +} + +func (r *MariadbDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "mariadb_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "database_name": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MariadbDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan MariadbDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.DatabaseName.IsNull() || plan.DatabaseName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "database_name is required.") + return + } + + instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MariadbDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MariadbDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MariadbDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MariadbDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state MariadbDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MariadbDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MariadbDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_database" == "" { + return + } + + instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(state.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "databaseName": resources_core.FormatString(state.DatabaseName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *MariadbDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/115_mariadb_resource.go b/generated/dev/go/115_mariadb_resource.go new file mode 100644 index 0000000..c514065 --- /dev/null +++ b/generated/dev/go/115_mariadb_resource.go @@ -0,0 +1,590 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mariadb +// Service ID: 115 + +var _ resource.Resource = &MariadbResource{} +var _ resource.ResourceWithModifyPlan = &MariadbResource{} +var _ resource.ResourceWithImportState = &MariadbResource{} + +type MariadbResource struct { + client *core.UniversalClient +} + +type MariadbModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AppVersion types.String `tfsdk:"app_version"` + AutoScale types.Bool `tfsdk:"auto_scale"` + AutoScalePercentage types.Int64 `tfsdk:"auto_scale_percentage"` + AutoScaleQuotaGb types.Int64 `tfsdk:"auto_scale_quota_gb"` + AutoScaleTechWindow types.Int64 `tfsdk:"auto_scale_tech_window"` + ExtBACKUPSCHEDULE types.String `tfsdk:"ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewMariadbResource() resource.Resource { + return &MariadbResource{} +} + +func (r *MariadbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mariadb" +} + +func (r *MariadbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9.4.0"), MarkdownDescription: "Версия MySQL Не изменяется после создания."}, + "auto_scale": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, + "auto_scale_percentage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема"}, + "auto_scale_quota_gb": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота для autoScale"}, + "auto_scale_tech_window": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, + "ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 * * * *"), MarkdownDescription: "Не обязательный. Время запуска бекапа"}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний (белый) IP для доступа к master"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores)"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MariadbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *MariadbModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *MariadbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan MariadbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить app_version", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 422: resources_core.FormatString(config.ResourceRealm), + 424: resources_core.FormatInt64(config.ResourceCPU), + 425: resources_core.FormatInt64(config.ResourceMemory), + 426: resources_core.FormatInt64(config.ResourceDisk), + 427: resources_core.FormatInt64(config.ResourceInstances), + 428: resources_core.FormatBool(config.NeedExternalAddressMaster), + 429: resources_core.FormatString(config.IpSpaceNameMaster), + 430: resources_core.FormatString(config.ExtBACKUPSCHEDULE), + 431: resources_core.FormatString(config.AppVersion), + 432: resources_core.FormatBool(config.AutoScale), + 433: resources_core.FormatInt64(config.AutoScalePercentage), + 434: resources_core.FormatInt64(config.AutoScaleTechWindow), + 435: resources_core.FormatInt64(config.AutoScaleQuotaGb), + 459: resources_core.FormatString(config.S3Uid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 115, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *MariadbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data MariadbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 422: resources_core.FormatString(data.ResourceRealm), + 424: resources_core.FormatInt64(data.ResourceCPU), + 425: resources_core.FormatInt64(data.ResourceMemory), + 426: resources_core.FormatInt64(data.ResourceDisk), + 427: resources_core.FormatInt64(data.ResourceInstances), + 428: resources_core.FormatBool(data.NeedExternalAddressMaster), + 429: resources_core.FormatString(data.IpSpaceNameMaster), + 430: resources_core.FormatString(data.ExtBACKUPSCHEDULE), + 431: resources_core.FormatString(data.AppVersion), + 432: resources_core.FormatBool(data.AutoScale), + 433: resources_core.FormatInt64(data.AutoScalePercentage), + 434: resources_core.FormatInt64(data.AutoScaleTechWindow), + 435: resources_core.FormatInt64(data.AutoScaleQuotaGb), + 459: resources_core.FormatString(data.S3Uid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 115, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "int64"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MariadbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MariadbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 115, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "int64"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MariadbModel + var state MariadbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPSCHEDULE.IsNull() != state.ExtBACKUPSCHEDULE.IsNull() || plan.ExtBACKUPSCHEDULE.IsUnknown() != state.ExtBACKUPSCHEDULE.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPSCHEDULE.IsNull() && !plan.ExtBACKUPSCHEDULE.IsUnknown() && !state.ExtBACKUPSCHEDULE.IsNull() && !state.ExtBACKUPSCHEDULE.IsUnknown() { + if plan.ExtBACKUPSCHEDULE.ValueString() != state.ExtBACKUPSCHEDULE.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScale.IsNull() != state.AutoScale.IsNull() || plan.AutoScale.IsUnknown() != state.AutoScale.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScale.IsNull() && !plan.AutoScale.IsUnknown() && !state.AutoScale.IsNull() && !state.AutoScale.IsUnknown() { + if plan.AutoScale.ValueBool() != state.AutoScale.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScalePercentage.IsNull() != state.AutoScalePercentage.IsNull() || plan.AutoScalePercentage.IsUnknown() != state.AutoScalePercentage.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScalePercentage.IsNull() && !plan.AutoScalePercentage.IsUnknown() && !state.AutoScalePercentage.IsNull() && !state.AutoScalePercentage.IsUnknown() { + if plan.AutoScalePercentage.ValueInt64() != state.AutoScalePercentage.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScaleTechWindow.IsNull() != state.AutoScaleTechWindow.IsNull() || plan.AutoScaleTechWindow.IsUnknown() != state.AutoScaleTechWindow.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScaleTechWindow.IsNull() && !plan.AutoScaleTechWindow.IsUnknown() && !state.AutoScaleTechWindow.IsNull() && !state.AutoScaleTechWindow.IsUnknown() { + if plan.AutoScaleTechWindow.ValueInt64() != state.AutoScaleTechWindow.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScaleQuotaGb.IsNull() != state.AutoScaleQuotaGb.IsNull() || plan.AutoScaleQuotaGb.IsUnknown() != state.AutoScaleQuotaGb.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScaleQuotaGb.IsNull() && !plan.AutoScaleQuotaGb.IsUnknown() && !state.AutoScaleQuotaGb.IsNull() && !state.AutoScaleQuotaGb.IsUnknown() { + if plan.AutoScaleQuotaGb.ValueInt64() != state.AutoScaleQuotaGb.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 436: resources_core.FormatInt64(plan.ResourceCPU), + 437: resources_core.FormatInt64(plan.ResourceMemory), + 438: resources_core.FormatInt64(plan.ResourceInstances), + 439: resources_core.FormatInt64(plan.ResourceDisk), + 440: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 441: resources_core.FormatString(plan.IpSpaceNameMaster), + 442: resources_core.FormatString(plan.ExtBACKUPSCHEDULE), + 443: resources_core.FormatBool(plan.AutoScale), + 444: resources_core.FormatInt64(plan.AutoScalePercentage), + 445: resources_core.FormatInt64(plan.AutoScaleTechWindow), + 446: resources_core.FormatInt64(plan.AutoScaleQuotaGb), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 115, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "int64"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MariadbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MariadbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *MariadbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *MariadbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/115_mariadb_user_resource.go b/generated/dev/go/115_mariadb_user_resource.go new file mode 100644 index 0000000..8c17b91 --- /dev/null +++ b/generated/dev/go/115_mariadb_user_resource.go @@ -0,0 +1,386 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mariadb +// Subresource: user + +var _ resource.Resource = &MariadbUserResource{} + +// MariadbUserResource manages a subresource via operations. +type MariadbUserResource struct { + client *core.UniversalClient +} + +type MariadbUserModel struct { + ID types.String `tfsdk:"id"` + MariadbID types.String `tfsdk:"mariadb_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AccessHosts types.String `tfsdk:"access_hosts"` + DatabaseName types.String `tfsdk:"database_name"` + Password types.String `tfsdk:"password"` + Username types.String `tfsdk:"username"` +} + +func NewMariadbUserResource() resource.Resource { + return &MariadbUserResource{} +} + +func (r *MariadbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mariadb_user" +} + +func (r *MariadbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "mariadb_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_hosts": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `%` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "database_name": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "password": schema.StringAttribute{Required: true, MarkdownDescription: "Секрет будет записан в хранилище секретов и не будет отображаться публично Не изменяется после создания. Изменение требует пересоздания.", Sensitive: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MariadbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan MariadbUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.AccessHosts.IsNull() || plan.AccessHosts.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "access_hosts is required.") + return + } + if plan.DatabaseName.IsNull() || plan.DatabaseName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "database_name is required.") + return + } + if plan.Password.IsNull() || plan.Password.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "password is required.") + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "password": resources_core.FormatString(plan.Password), + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "password", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "password": resources_core.FormatString(plan.Password), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MariadbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MariadbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MariadbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MariadbUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state MariadbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "password": resources_core.FormatString(plan.Password), + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MariadbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MariadbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(state.AccessHosts), + "databaseName": resources_core.FormatString(state.DatabaseName), + "password": resources_core.FormatString(state.Password), + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "password", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *MariadbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/116_kafka_resource.go b/generated/dev/go/116_kafka_resource.go new file mode 100644 index 0000000..e4bbe7f --- /dev/null +++ b/generated/dev/go/116_kafka_resource.go @@ -0,0 +1,480 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: kafka +// Service ID: 116 + +var _ resource.Resource = &KafkaResource{} +var _ resource.ResourceWithModifyPlan = &KafkaResource{} +var _ resource.ResourceWithImportState = &KafkaResource{} + +type KafkaResource struct { + client *core.UniversalClient +} + +type KafkaModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewKafkaResource() resource.Resource { + return &KafkaResource{} +} + +func (r *KafkaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_kafka" +} + +func (r *KafkaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`"}, + "resource_c_p_u": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2000`"}, + "resource_disk": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `20`"}, + "resource_instances": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2`"}, + "resource_memory": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2048`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *KafkaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *KafkaModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *KafkaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan KafkaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceCPU.IsNull() || config.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if config.ResourceDisk.IsNull() || config.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if config.ResourceInstances.IsNull() || config.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if config.ResourceMemory.IsNull() || config.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 461: resources_core.FormatInt64(config.ResourceInstances), + 462: resources_core.FormatInt64(config.ResourceMemory), + 463: resources_core.FormatInt64(config.ResourceCPU), + 464: resources_core.FormatInt64(config.ResourceDisk), + 465: resources_core.FormatBool(config.NeedExternalAddressMaster), + 466: resources_core.FormatString(config.IpSpaceNameMaster), + 469: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 116, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *KafkaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data KafkaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceCPU.IsNull() || data.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if data.ResourceDisk.IsNull() || data.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if data.ResourceInstances.IsNull() || data.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if data.ResourceMemory.IsNull() || data.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 461: resources_core.FormatInt64(data.ResourceInstances), + 462: resources_core.FormatInt64(data.ResourceMemory), + 463: resources_core.FormatInt64(data.ResourceCPU), + 464: resources_core.FormatInt64(data.ResourceDisk), + 465: resources_core.FormatBool(data.NeedExternalAddressMaster), + 466: resources_core.FormatString(data.IpSpaceNameMaster), + 469: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 116, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state KafkaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 116, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan KafkaModel + var state KafkaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 501: resources_core.FormatInt64(plan.ResourceInstances), + 502: resources_core.FormatInt64(plan.ResourceMemory), + 503: resources_core.FormatInt64(plan.ResourceCPU), + 504: resources_core.FormatInt64(plan.ResourceDisk), + 505: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 506: resources_core.FormatString(plan.IpSpaceNameMaster), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 116, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state KafkaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *KafkaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *KafkaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/116_kafka_topic_resource.go b/generated/dev/go/116_kafka_topic_resource.go new file mode 100644 index 0000000..0b05fff --- /dev/null +++ b/generated/dev/go/116_kafka_topic_resource.go @@ -0,0 +1,368 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: kafka +// Subresource: topic + +var _ resource.Resource = &KafkaTopicResource{} + +// KafkaTopicResource manages a subresource via operations. +type KafkaTopicResource struct { + client *core.UniversalClient +} + +type KafkaTopicModel struct { + ID types.String `tfsdk:"id"` + KafkaID types.String `tfsdk:"kafka_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + NameTopic types.String `tfsdk:"name_topic"` + Partitions types.Int64 `tfsdk:"partitions"` + Replicas types.Int64 `tfsdk:"replicas"` +} + +func NewKafkaTopicResource() resource.Resource { + return &KafkaTopicResource{} +} + +func (r *KafkaTopicResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_kafka_topic" +} + +func (r *KafkaTopicResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "kafka_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций можно увеличить, но уменьшить их нельзя."}, + "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество реплик не может привышать количество реплик брокера"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *KafkaTopicResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan KafkaTopicModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if plan.Partitions.IsNull() || plan.Partitions.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") + return + } + if plan.Replicas.IsNull() || plan.Replicas.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + } + identityCodes := []string{ + "nameTopic", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + "partitions": resources_core.FormatInt64(plan.Partitions), + "replicas": resources_core.FormatInt64(plan.Replicas), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaTopicResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state KafkaTopicModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaTopicResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan KafkaTopicModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "modify_topic" == "" { + var state KafkaTopicModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + "partitions": resources_core.FormatInt64(plan.Partitions), + "replicas": resources_core.FormatInt64(plan.Replicas), + }) + + idParams := map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_topic", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaTopicResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state KafkaTopicModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_topic" == "" { + return + } + + instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "nameTopic": resources_core.FormatString(state.NameTopic), + } + identityCodes := []string{ + "nameTopic", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(state.NameTopic), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_topic", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *KafkaTopicResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/116_kafka_user_resource.go b/generated/dev/go/116_kafka_user_resource.go new file mode 100644 index 0000000..e1bf992 --- /dev/null +++ b/generated/dev/go/116_kafka_user_resource.go @@ -0,0 +1,384 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: kafka +// Subresource: user + +var _ resource.Resource = &KafkaUserResource{} + +// KafkaUserResource manages a subresource via operations. +type KafkaUserResource struct { + client *core.UniversalClient +} + +type KafkaUserModel struct { + ID types.String `tfsdk:"id"` + KafkaID types.String `tfsdk:"kafka_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AccessHosts types.String `tfsdk:"access_hosts"` + Group types.String `tfsdk:"group"` + NameTopic types.String `tfsdk:"name_topic"` + Operations types.String `tfsdk:"operations"` + Username types.String `tfsdk:"username"` +} + +func NewKafkaUserResource() resource.Resource { + return &KafkaUserResource{} +} + +func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_kafka_user" +} + +func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "kafka_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_hosts": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `192.168.1.0/24` или `*`"}, + "group": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `order-consumers` или `*`"}, + "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`"}, + "operations": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `Create, Describe, Read, Write`"}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `username-admin` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *KafkaUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan KafkaUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.AccessHosts.IsNull() || plan.AccessHosts.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "access_hosts is required.") + return + } + if plan.Group.IsNull() || plan.Group.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "group is required.") + return + } + if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if plan.Operations.IsNull() || plan.Operations.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "operations is required.") + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "nameTopic": resources_core.FormatString(plan.NameTopic), + "operations": resources_core.FormatString(plan.Operations), + "group": resources_core.FormatString(plan.Group), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state KafkaUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan KafkaUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "modify_user" == "" { + var state KafkaUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + "operations": resources_core.FormatString(plan.Operations), + "group": resources_core.FormatString(plan.Group), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "username": resources_core.FormatString(plan.Username), + }) + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state KafkaUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *KafkaUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/119_akhq_resource.go b/generated/dev/go/119_akhq_resource.go new file mode 100644 index 0000000..d7e6a84 --- /dev/null +++ b/generated/dev/go/119_akhq_resource.go @@ -0,0 +1,496 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: akhq +// Service ID: 119 + +var _ resource.Resource = &AkhqResource{} +var _ resource.ResourceWithModifyPlan = &AkhqResource{} +var _ resource.ResourceWithImportState = &AkhqResource{} + +type AkhqResource struct { + client *core.UniversalClient +} + +type AkhqModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + KafkaUid types.String `tfsdk:"kafka_uid"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewAkhqResource() resource.Resource { + return &AkhqResource{} +} + +func (r *AkhqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_akhq" +} + +func (r *AkhqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "kafka_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать кластер из списка"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `1000` Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `1024` Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *AkhqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *AkhqModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *AkhqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan AkhqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, plan.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != plan.KafkaUid.ValueString() { + plan.KafkaUid = types.StringValue(resolvedKafkaUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.KafkaUid.IsNull() || config.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { + config.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + params := map[int]string{ + 550: resources_core.FormatString(config.ResourceRealm), + 551: resources_core.FormatInt64(config.ResourceMemory), + 552: resources_core.FormatInt64(config.ResourceCPU), + 553: resources_core.FormatString(config.Domain), + 554: resources_core.FormatInt64(config.ResourceInstances), + 555: resources_core.FormatString(config.KafkaUid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 119, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *AkhqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data AkhqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Domain.IsNull() || data.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { + data.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 550: resources_core.FormatString(data.ResourceRealm), + 551: resources_core.FormatInt64(data.ResourceMemory), + 552: resources_core.FormatInt64(data.ResourceCPU), + 553: resources_core.FormatString(data.Domain), + 554: resources_core.FormatInt64(data.ResourceInstances), + 555: resources_core.FormatString(data.KafkaUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 119, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { + state.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *AkhqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state AkhqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 119, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { + newState.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan AkhqModel + var state AkhqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.KafkaUid.IsNull() != state.KafkaUid.IsNull() || plan.KafkaUid.IsUnknown() != state.KafkaUid.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + if plan.KafkaUid.ValueString() != state.KafkaUid.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 565: resources_core.FormatString(plan.KafkaUid), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 119, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { + state.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *AkhqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state AkhqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *AkhqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *AkhqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/120_clickhouse_database_resource.go b/generated/dev/go/120_clickhouse_database_resource.go new file mode 100644 index 0000000..e177abb --- /dev/null +++ b/generated/dev/go/120_clickhouse_database_resource.go @@ -0,0 +1,350 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: clickhouse +// Subresource: database + +var _ resource.Resource = &ClickhouseDatabaseResource{} + +// ClickhouseDatabaseResource manages a subresource via operations. +type ClickhouseDatabaseResource struct { + client *core.UniversalClient +} + +type ClickhouseDatabaseModel struct { + ID types.String `tfsdk:"id"` + ClickhouseID types.String `tfsdk:"clickhouse_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + DatabaseName types.String `tfsdk:"database_name"` +} + +func NewClickhouseDatabaseResource() resource.Resource { + return &ClickhouseDatabaseResource{} +} + +func (r *ClickhouseDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_clickhouse_database" +} + +func (r *ClickhouseDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "clickhouse_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "database_name": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ClickhouseDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan ClickhouseDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.DatabaseName.IsNull() || plan.DatabaseName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "database_name is required.") + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ClickhouseDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ClickhouseDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state ClickhouseDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ClickhouseDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_database" == "" { + return + } + + instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(state.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "databaseName": resources_core.FormatString(state.DatabaseName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *ClickhouseDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/120_clickhouse_resource.go b/generated/dev/go/120_clickhouse_resource.go new file mode 100644 index 0000000..9579b89 --- /dev/null +++ b/generated/dev/go/120_clickhouse_resource.go @@ -0,0 +1,628 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: clickhouse +// Service ID: 120 + +var _ resource.Resource = &ClickhouseResource{} +var _ resource.ResourceWithModifyPlan = &ClickhouseResource{} +var _ resource.ResourceWithImportState = &ClickhouseResource{} + +type ClickhouseResource struct { + client *core.UniversalClient +} + +type ClickhouseModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AppVersion types.String `tfsdk:"app_version"` + ExtBACKUPNUMTORETAIN types.Int64 `tfsdk:"ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n"` + ExtBACKUPSCHEDULE types.String `tfsdk:"ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedCreateKeeper types.Bool `tfsdk:"need_create_keeper"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceCPUKeeper types.Int64 `tfsdk:"resource_c_p_u_keeper"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceInstancesKeeper types.Int64 `tfsdk:"resource_instances_keeper"` + ResourceInstancesShards types.Int64 `tfsdk:"resource_instances_shards"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceMemoryKeeper types.Int64 `tfsdk:"resource_memory_keeper"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewClickhouseResource() resource.Resource { + return &ClickhouseResource{} +} + +func (r *ClickhouseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_clickhouse" +} + +func (r *ClickhouseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("25.10"), MarkdownDescription: "Версия `clickhouse-server`"}, + "ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7), MarkdownDescription: "Пример: `7`"}, + "ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *"), MarkdownDescription: "Принимает формат `CRON`"}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, + "need_create_keeper": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходим для кластеризации ClickHouse"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Позволяет подключиться извне к ClickHouse"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `1000`"}, + "resource_c_p_u_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50), MarkdownDescription: "Пример: `100`"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `100`"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `3`"}, + "resource_instances_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_instances_shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1536), MarkdownDescription: "Пример: `2048`"}, + "resource_memory_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(152), MarkdownDescription: "Пример: `256`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ClickhouseResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *ClickhouseModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *ClickhouseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan ClickhouseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 572: resources_core.FormatString(config.ResourceRealm), + 573: resources_core.FormatString(config.S3Uid), + 574: resources_core.FormatInt64(config.ResourceInstances), + 575: resources_core.FormatInt64(config.ResourceInstancesShards), + 576: resources_core.FormatInt64(config.ResourceCPU), + 577: resources_core.FormatInt64(config.ResourceMemory), + 578: resources_core.FormatString(config.AppVersion), + 579: resources_core.FormatInt64(config.ResourceDisk), + 580: resources_core.FormatBool(config.NeedExternalAddressMaster), + 581: resources_core.FormatString(config.IpSpaceNameMaster), + 582: resources_core.FormatBool(config.NeedCreateKeeper), + 583: resources_core.FormatInt64(config.ResourceCPUKeeper), + 584: resources_core.FormatInt64(config.ResourceMemoryKeeper), + 585: resources_core.FormatInt64(config.ResourceInstancesKeeper), + 586: resources_core.FormatString(config.ExtBACKUPSCHEDULE), + 587: resources_core.FormatInt64(config.ExtBACKUPNUMTORETAIN), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 120, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *ClickhouseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data ClickhouseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 572: resources_core.FormatString(data.ResourceRealm), + 573: resources_core.FormatString(data.S3Uid), + 574: resources_core.FormatInt64(data.ResourceInstances), + 575: resources_core.FormatInt64(data.ResourceInstancesShards), + 576: resources_core.FormatInt64(data.ResourceCPU), + 577: resources_core.FormatInt64(data.ResourceMemory), + 578: resources_core.FormatString(data.AppVersion), + 579: resources_core.FormatInt64(data.ResourceDisk), + 580: resources_core.FormatBool(data.NeedExternalAddressMaster), + 581: resources_core.FormatString(data.IpSpaceNameMaster), + 582: resources_core.FormatBool(data.NeedCreateKeeper), + 583: resources_core.FormatInt64(data.ResourceCPUKeeper), + 584: resources_core.FormatInt64(data.ResourceMemoryKeeper), + 585: resources_core.FormatInt64(data.ResourceInstancesKeeper), + 586: resources_core.FormatString(data.ExtBACKUPSCHEDULE), + 587: resources_core.FormatInt64(data.ExtBACKUPNUMTORETAIN), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 120, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ClickhouseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 120, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ClickhouseModel + var state ClickhouseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstancesShards.IsNull() != state.ResourceInstancesShards.IsNull() || plan.ResourceInstancesShards.IsUnknown() != state.ResourceInstancesShards.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstancesShards.IsNull() && !plan.ResourceInstancesShards.IsUnknown() && !state.ResourceInstancesShards.IsNull() && !state.ResourceInstancesShards.IsUnknown() { + if plan.ResourceInstancesShards.ValueInt64() != state.ResourceInstancesShards.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedCreateKeeper.IsNull() != state.NeedCreateKeeper.IsNull() || plan.NeedCreateKeeper.IsUnknown() != state.NeedCreateKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedCreateKeeper.IsNull() && !plan.NeedCreateKeeper.IsUnknown() && !state.NeedCreateKeeper.IsNull() && !state.NeedCreateKeeper.IsUnknown() { + if plan.NeedCreateKeeper.ValueBool() != state.NeedCreateKeeper.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPUKeeper.IsNull() != state.ResourceCPUKeeper.IsNull() || plan.ResourceCPUKeeper.IsUnknown() != state.ResourceCPUKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPUKeeper.IsNull() && !plan.ResourceCPUKeeper.IsUnknown() && !state.ResourceCPUKeeper.IsNull() && !state.ResourceCPUKeeper.IsUnknown() { + if plan.ResourceCPUKeeper.ValueInt64() != state.ResourceCPUKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemoryKeeper.IsNull() != state.ResourceMemoryKeeper.IsNull() || plan.ResourceMemoryKeeper.IsUnknown() != state.ResourceMemoryKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemoryKeeper.IsNull() && !plan.ResourceMemoryKeeper.IsUnknown() && !state.ResourceMemoryKeeper.IsNull() && !state.ResourceMemoryKeeper.IsUnknown() { + if plan.ResourceMemoryKeeper.ValueInt64() != state.ResourceMemoryKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstancesKeeper.IsNull() != state.ResourceInstancesKeeper.IsNull() || plan.ResourceInstancesKeeper.IsUnknown() != state.ResourceInstancesKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstancesKeeper.IsNull() && !plan.ResourceInstancesKeeper.IsUnknown() && !state.ResourceInstancesKeeper.IsNull() && !state.ResourceInstancesKeeper.IsUnknown() { + if plan.ResourceInstancesKeeper.ValueInt64() != state.ResourceInstancesKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPSCHEDULE.IsNull() != state.ExtBACKUPSCHEDULE.IsNull() || plan.ExtBACKUPSCHEDULE.IsUnknown() != state.ExtBACKUPSCHEDULE.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPSCHEDULE.IsNull() && !plan.ExtBACKUPSCHEDULE.IsUnknown() && !state.ExtBACKUPSCHEDULE.IsNull() && !state.ExtBACKUPSCHEDULE.IsUnknown() { + if plan.ExtBACKUPSCHEDULE.ValueString() != state.ExtBACKUPSCHEDULE.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPNUMTORETAIN.IsNull() != state.ExtBACKUPNUMTORETAIN.IsNull() || plan.ExtBACKUPNUMTORETAIN.IsUnknown() != state.ExtBACKUPNUMTORETAIN.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPNUMTORETAIN.IsNull() && !plan.ExtBACKUPNUMTORETAIN.IsUnknown() && !state.ExtBACKUPNUMTORETAIN.IsNull() && !state.ExtBACKUPNUMTORETAIN.IsUnknown() { + if plan.ExtBACKUPNUMTORETAIN.ValueInt64() != state.ExtBACKUPNUMTORETAIN.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 603: resources_core.FormatInt64(plan.ResourceInstances), + 604: resources_core.FormatInt64(plan.ResourceInstancesShards), + 605: resources_core.FormatInt64(plan.ResourceCPU), + 606: resources_core.FormatInt64(plan.ResourceMemory), + 607: resources_core.FormatString(plan.AppVersion), + 608: resources_core.FormatInt64(plan.ResourceDisk), + 609: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 610: resources_core.FormatString(plan.IpSpaceNameMaster), + 611: resources_core.FormatBool(plan.NeedCreateKeeper), + 612: resources_core.FormatInt64(plan.ResourceCPUKeeper), + 613: resources_core.FormatInt64(plan.ResourceMemoryKeeper), + 614: resources_core.FormatInt64(plan.ResourceInstancesKeeper), + 615: resources_core.FormatString(plan.ExtBACKUPSCHEDULE), + 616: resources_core.FormatInt64(plan.ExtBACKUPNUMTORETAIN), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 120, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ClickhouseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *ClickhouseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *ClickhouseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/120_clickhouse_user_resource.go b/generated/dev/go/120_clickhouse_user_resource.go new file mode 100644 index 0000000..30a38bf --- /dev/null +++ b/generated/dev/go/120_clickhouse_user_resource.go @@ -0,0 +1,367 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: clickhouse +// Subresource: user + +var _ resource.Resource = &ClickhouseUserResource{} + +// ClickhouseUserResource manages a subresource via operations. +type ClickhouseUserResource struct { + client *core.UniversalClient +} + +type ClickhouseUserModel struct { + ID types.String `tfsdk:"id"` + ClickhouseID types.String `tfsdk:"clickhouse_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AccessHosts types.String `tfsdk:"access_hosts"` + DatabaseName types.String `tfsdk:"database_name"` + Username types.String `tfsdk:"username"` +} + +func NewClickhouseUserResource() resource.Resource { + return &ClickhouseUserResource{} +} + +func (r *ClickhouseUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_clickhouse_user" +} + +func (r *ClickhouseUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "clickhouse_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("::/0"), MarkdownDescription: "Пример: `::/0` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "database_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"database\"]"), MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ClickhouseUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan ClickhouseUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ClickhouseUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ClickhouseUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state ClickhouseUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ClickhouseUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(state.AccessHosts), + "databaseName": resources_core.FormatString(state.DatabaseName), + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *ClickhouseUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/12_s3_resource.go b/generated/dev/go/12_s3_resource.go new file mode 100644 index 0000000..3f3b56b --- /dev/null +++ b/generated/dev/go/12_s3_resource.go @@ -0,0 +1,412 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: s3 +// Service ID: 12 + +var _ resource.Resource = &S3Resource{} +var _ resource.ResourceWithModifyPlan = &S3Resource{} +var _ resource.ResourceWithImportState = &S3Resource{} + +type S3Resource struct { + client *core.UniversalClient +} + +type S3Model struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + DisplayName types.String `tfsdk:"display_name"` + MaxBucketsPerUser types.Int64 `tfsdk:"max_buckets_per_user"` + MaxObjectsPerBucket types.Int64 `tfsdk:"max_objects_per_bucket"` + MaxSizeGbPerUser types.Int64 `tfsdk:"max_size_gb_per_user"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewS3Resource() resource.Resource { + return &S3Resource{} +} + +func (r *S3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_s3" +} + +func (r *S3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "display_name": schema.StringAttribute{Required: true, MarkdownDescription: "Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента Не изменяется после создания."}, + "max_buckets_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на пользователя. Количество бакетов пользователя. Передаём руками"}, + "max_objects_per_bucket": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на бакет. Кол-во объектов. Передаём руками"}, + "max_size_gb_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "maxSizeGb. Передаём руками"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *S3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *S3Model + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *S3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan S3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.DisplayName.IsNull() && !plan.DisplayName.IsUnknown() && !state.DisplayName.IsNull() && !state.DisplayName.IsUnknown() { + if plan.DisplayName.ValueString() != state.DisplayName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить display_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.DisplayName.IsNull() || config.DisplayName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 163: resources_core.FormatString(config.ResourceRealm), + 320: resources_core.FormatString(config.DisplayName), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 12, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *S3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data S3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.DisplayName.IsNull() || data.DisplayName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 163: resources_core.FormatString(data.ResourceRealm), + 320: resources_core.FormatString(data.DisplayName), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 12, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "displayName", Field: "DisplayName", Type: "string"}, + {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, + {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, + {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state S3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 12, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "displayName", Field: "DisplayName", Type: "string"}, + {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, + {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, + {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan S3Model + var state S3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.MaxSizeGbPerUser.IsNull() != state.MaxSizeGbPerUser.IsNull() || plan.MaxSizeGbPerUser.IsUnknown() != state.MaxSizeGbPerUser.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MaxSizeGbPerUser.IsNull() && !plan.MaxSizeGbPerUser.IsUnknown() && !state.MaxSizeGbPerUser.IsNull() && !state.MaxSizeGbPerUser.IsUnknown() { + if plan.MaxSizeGbPerUser.ValueInt64() != state.MaxSizeGbPerUser.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MaxObjectsPerBucket.IsNull() != state.MaxObjectsPerBucket.IsNull() || plan.MaxObjectsPerBucket.IsUnknown() != state.MaxObjectsPerBucket.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MaxObjectsPerBucket.IsNull() && !plan.MaxObjectsPerBucket.IsUnknown() && !state.MaxObjectsPerBucket.IsNull() && !state.MaxObjectsPerBucket.IsUnknown() { + if plan.MaxObjectsPerBucket.ValueInt64() != state.MaxObjectsPerBucket.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MaxBucketsPerUser.IsNull() != state.MaxBucketsPerUser.IsNull() || plan.MaxBucketsPerUser.IsUnknown() != state.MaxBucketsPerUser.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MaxBucketsPerUser.IsNull() && !plan.MaxBucketsPerUser.IsUnknown() && !state.MaxBucketsPerUser.IsNull() && !state.MaxBucketsPerUser.IsUnknown() { + if plan.MaxBucketsPerUser.ValueInt64() != state.MaxBucketsPerUser.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), + 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), + 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 12, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "displayName", Field: "DisplayName", Type: "string"}, + {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, + {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, + {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state S3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *S3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *S3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/12_s3_sub_user_resource.go b/generated/dev/go/12_s3_sub_user_resource.go new file mode 100644 index 0000000..c9d01a6 --- /dev/null +++ b/generated/dev/go/12_s3_sub_user_resource.go @@ -0,0 +1,362 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: s3 +// Subresource: sub_user + +var _ resource.Resource = &S3SubUserResource{} + +// S3SubUserResource manages a subresource via operations. +type S3SubUserResource struct { + client *core.UniversalClient +} + +type S3SubUserModel struct { + ID types.String `tfsdk:"id"` + S3ID types.String `tfsdk:"s3_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + SubUserAccess types.String `tfsdk:"sub_user_access"` + SubUserName types.String `tfsdk:"sub_user_name"` +} + +func NewS3SubUserResource() resource.Resource { + return &S3SubUserResource{} +} + +func (r *S3SubUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_s3_sub_user" +} + +func (r *S3SubUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "s3_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "sub_user_access": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "sub_user_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *S3SubUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan S3SubUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.SubUserAccess.IsNull() || plan.SubUserAccess.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "sub_user_access is required.") + return + } + if plan.SubUserName.IsNull() || plan.SubUserName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "sub_user_name is required.") + return + } + + instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "subUserAccess": resources_core.FormatString(plan.SubUserAccess), + "subUserName": resources_core.FormatString(plan.SubUserName), + } + identityCodes := []string{ + "subUserAccess", + "subUserName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "subUserName": resources_core.FormatString(plan.SubUserName), + "subUserAccess": resources_core.FormatString(plan.SubUserAccess), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *S3SubUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state S3SubUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3SubUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan S3SubUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state S3SubUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "subUserAccess": resources_core.FormatString(plan.SubUserAccess), + "subUserName": resources_core.FormatString(plan.SubUserName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *S3SubUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state S3SubUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_sub_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.S3ID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "subUserAccess": resources_core.FormatString(state.SubUserAccess), + "subUserName": resources_core.FormatString(state.SubUserName), + } + identityCodes := []string{ + "subUserAccess", + "subUserName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "subUserName": resources_core.FormatString(state.SubUserName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_sub_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *S3SubUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/13_s3bucket_resource.go b/generated/dev/go/13_s3bucket_resource.go new file mode 100644 index 0000000..06bd07d --- /dev/null +++ b/generated/dev/go/13_s3bucket_resource.go @@ -0,0 +1,450 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: s3bucket +// Service ID: 13 + +var _ resource.Resource = &S3bucketResource{} +var _ resource.ResourceWithModifyPlan = &S3bucketResource{} +var _ resource.ResourceWithImportState = &S3bucketResource{} + +type S3bucketResource struct { + client *core.UniversalClient +} + +type S3bucketModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + BucketName types.String `tfsdk:"bucket_name"` + CorsAll types.Bool `tfsdk:"cors_all"` + ListAll types.Bool `tfsdk:"list_all"` + MaxSize types.String `tfsdk:"max_size"` + Placement types.String `tfsdk:"placement"` + ReadAll types.Bool `tfsdk:"read_all"` + S3UserUid types.String `tfsdk:"s3_user_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewS3bucketResource() resource.Resource { + return &S3bucketResource{} +} + +func (r *S3bucketResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_s3bucket" +} + +func (r *S3bucketResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "bucket_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя бакета s3 Не изменяется после создания."}, + "cors_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS Не изменяется после создания."}, + "list_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. Не изменяется после создания."}, + "max_size": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("-1"), MarkdownDescription: "Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes Не изменяется после создания."}, + "placement": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("HOT"), MarkdownDescription: "Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения Не изменяется после создания."}, + "read_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. Не изменяется после создания."}, + "s3_user_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UID Корневой услуги S3. На основе него будет создан бакет. Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *S3bucketResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *S3bucketModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *S3bucketModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan S3bucketModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() { + resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3UserUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) + } else if resolvedS3UserUid != "" && resolvedS3UserUid != plan.S3UserUid.ValueString() { + plan.S3UserUid = types.StringValue(resolvedS3UserUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.BucketName.IsNull() && !plan.BucketName.IsUnknown() && !state.BucketName.IsNull() && !state.BucketName.IsUnknown() { + if plan.BucketName.ValueString() != state.BucketName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить bucket_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.CorsAll.IsNull() && !plan.CorsAll.IsUnknown() && !state.CorsAll.IsNull() && !state.CorsAll.IsUnknown() { + if plan.CorsAll.ValueBool() != state.CorsAll.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить cors_all", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ListAll.IsNull() && !plan.ListAll.IsUnknown() && !state.ListAll.IsNull() && !state.ListAll.IsUnknown() { + if plan.ListAll.ValueBool() != state.ListAll.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить list_all", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.MaxSize.IsNull() && !plan.MaxSize.IsUnknown() && !state.MaxSize.IsNull() && !state.MaxSize.IsUnknown() { + if plan.MaxSize.ValueString() != state.MaxSize.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить max_size", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Placement.IsNull() && !plan.Placement.IsUnknown() && !state.Placement.IsNull() && !state.Placement.IsUnknown() { + if plan.Placement.ValueString() != state.Placement.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить placement", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ReadAll.IsNull() && !plan.ReadAll.IsUnknown() && !state.ReadAll.IsNull() && !state.ReadAll.IsUnknown() { + if plan.ReadAll.ValueBool() != state.ReadAll.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить read_all", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() && !state.S3UserUid.IsNull() && !state.S3UserUid.IsUnknown() { + if plan.S3UserUid.ValueString() != state.S3UserUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_user_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.BucketName.IsNull() || config.BucketName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") + return + } + if config.S3UserUid.IsNull() || config.S3UserUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3UserUid.IsNull() && !config.S3UserUid.IsUnknown() { + resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3UserUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) + } else if resolvedS3UserUid != "" && resolvedS3UserUid != config.S3UserUid.ValueString() { + config.S3UserUid = types.StringValue(resolvedS3UserUid) + } + } + params := map[int]string{ + 124: resources_core.FormatString(config.S3UserUid), + 125: resources_core.FormatString(config.BucketName), + 126: resources_core.FormatString(config.MaxSize), + 127: resources_core.FormatBool(config.ReadAll), + 128: resources_core.FormatBool(config.ListAll), + 129: resources_core.FormatBool(config.CorsAll), + 130: resources_core.FormatString(config.Placement), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 13, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *S3bucketResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data S3bucketModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.BucketName.IsNull() || data.BucketName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") + return + } + if data.S3UserUid.IsNull() || data.S3UserUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 124: resources_core.FormatString(data.S3UserUid), + 125: resources_core.FormatString(data.BucketName), + 126: resources_core.FormatString(data.MaxSize), + 127: resources_core.FormatBool(data.ReadAll), + 128: resources_core.FormatBool(data.ListAll), + 129: resources_core.FormatBool(data.CorsAll), + 130: resources_core.FormatString(data.Placement), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 13, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "bucketName", Field: "BucketName", Type: "string"}, + {Code: "corsAll", Field: "CorsAll", Type: "bool"}, + {Code: "listAll", Field: "ListAll", Type: "bool"}, + {Code: "maxSize", Field: "MaxSize", Type: "string"}, + {Code: "placement", Field: "Placement", Type: "string"}, + {Code: "readAll", Field: "ReadAll", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3bucketResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state S3bucketModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 13, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "bucketName", Field: "BucketName", Type: "string"}, + {Code: "corsAll", Field: "CorsAll", Type: "bool"}, + {Code: "listAll", Field: "ListAll", Type: "bool"}, + {Code: "maxSize", Field: "MaxSize", Type: "string"}, + {Code: "placement", Field: "Placement", Type: "string"}, + {Code: "readAll", Field: "ReadAll", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan S3bucketModel + var state S3bucketModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 13, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "bucketName", Field: "BucketName", Type: "string"}, + {Code: "corsAll", Field: "CorsAll", Type: "bool"}, + {Code: "listAll", Field: "ListAll", Type: "bool"}, + {Code: "maxSize", Field: "MaxSize", Type: "string"}, + {Code: "placement", Field: "Placement", Type: "string"}, + {Code: "readAll", Field: "ReadAll", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state S3bucketModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *S3bucketResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *S3bucketResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/149_valo_tenant_resource.go b/generated/dev/go/149_valo_tenant_resource.go new file mode 100644 index 0000000..2e2a5c7 --- /dev/null +++ b/generated/dev/go/149_valo_tenant_resource.go @@ -0,0 +1,409 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: valo_tenant +// Service ID: 149 + +var _ resource.Resource = &ValoTenantResource{} +var _ resource.ResourceWithModifyPlan = &ValoTenantResource{} +var _ resource.ResourceWithImportState = &ValoTenantResource{} + +type ValoTenantResource struct { + client *core.UniversalClient +} + +type ValoTenantModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Storage types.Int64 `tfsdk:"s3_storage"` + ValoOptions types.String `tfsdk:"valo_options"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewValoTenantResource() resource.Resource { + return &ValoTenantResource{} +} + +func (r *ValoTenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_valo_tenant" +} + +func (r *ValoTenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Будет добавлено в зону valo.ngcloud.ru Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "s3_storage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Параметр указывается в `Gb`"}, + "valo_options": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ValoTenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *ValoTenantModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *ValoTenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan ValoTenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 655: resources_core.FormatString(config.ResourceRealm), + 656: resources_core.FormatString(config.Domain), + 657: resources_core.FormatString(config.ValoOptions), + 658: resources_core.FormatInt64(config.S3Storage), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 149, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *ValoTenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data ValoTenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Domain.IsNull() || data.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 655: resources_core.FormatString(data.ResourceRealm), + 656: resources_core.FormatString(data.Domain), + 657: resources_core.FormatString(data.ValoOptions), + 658: resources_core.FormatInt64(data.S3Storage), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 149, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, + {Code: "valoOptions", Field: "ValoOptions", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ValoTenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ValoTenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 149, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, + {Code: "valoOptions", Field: "ValoOptions", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ValoTenantModel + var state ValoTenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ValoOptions.IsNull() != state.ValoOptions.IsNull() || plan.ValoOptions.IsUnknown() != state.ValoOptions.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ValoOptions.IsNull() && !plan.ValoOptions.IsUnknown() && !state.ValoOptions.IsNull() && !state.ValoOptions.IsUnknown() { + if plan.ValoOptions.ValueString() != state.ValoOptions.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.S3Storage.IsNull() != state.S3Storage.IsNull() || plan.S3Storage.IsUnknown() != state.S3Storage.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.S3Storage.IsNull() && !plan.S3Storage.IsUnknown() && !state.S3Storage.IsNull() && !state.S3Storage.IsUnknown() { + if plan.S3Storage.ValueInt64() != state.S3Storage.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 659: resources_core.FormatString(plan.ValoOptions), + 660: resources_core.FormatInt64(plan.S3Storage), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 149, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, + {Code: "valoOptions", Field: "ValoOptions", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ValoTenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ValoTenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *ValoTenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *ValoTenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_reconcile_action.go b/generated/dev/go/150_k8s_sthutrval_cluster_reconcile_action.go similarity index 100% rename from provider/internal/resources_gen/150_k8s_sthutrval_cluster_reconcile_action.go rename to generated/dev/go/150_k8s_sthutrval_cluster_reconcile_action.go diff --git a/generated/dev/go/150_k8s_sthutrval_cluster_resource.go b/generated/dev/go/150_k8s_sthutrval_cluster_resource.go new file mode 100644 index 0000000..d77b29c --- /dev/null +++ b/generated/dev/go/150_k8s_sthutrval_cluster_resource.go @@ -0,0 +1,439 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_sthutrval_cluster +// Service ID: 150 + +var _ resource.Resource = &K8sSthutrvalClusterResource{} +var _ resource.ResourceWithModifyPlan = &K8sSthutrvalClusterResource{} +var _ resource.ResourceWithImportState = &K8sSthutrvalClusterResource{} + +type K8sSthutrvalClusterResource struct { + client *core.UniversalClient +} + +type K8sSthutrvalClusterModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AccessConfiguration types.String `tfsdk:"access_configuration"` + ClusterConfiguration types.String `tfsdk:"cluster_configuration"` + ControlPlaneConfiguration types.String `tfsdk:"control_plane_configuration"` + StartupConfiguration types.String `tfsdk:"startup_configuration"` + WorkerConfiguration types.String `tfsdk:"worker_configuration"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewK8sSthutrvalClusterResource() resource.Resource { + return &K8sSthutrvalClusterResource{} +} + +func (r *K8sSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster" +} + +func (r *K8sSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_configuration": schema.StringAttribute{Required: true}, + "cluster_configuration": schema.StringAttribute{Required: true}, + "control_plane_configuration": schema.StringAttribute{Required: true}, + "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]")}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.StartupConfiguration.IsNull() && !plan.StartupConfiguration.IsUnknown() && !state.StartupConfiguration.IsNull() && !state.StartupConfiguration.IsUnknown() { + if plan.StartupConfiguration.ValueString() != state.StartupConfiguration.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить startup_configuration", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.AccessConfiguration.IsNull() || config.AccessConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "access_configuration is required.") + return + } + if config.ClusterConfiguration.IsNull() || config.ClusterConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "cluster_configuration is required.") + return + } + if config.ControlPlaneConfiguration.IsNull() || config.ControlPlaneConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "control_plane_configuration is required.") + return + } + if config.StartupConfiguration.IsNull() || config.StartupConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 750: resources_core.FormatString(config.StartupConfiguration), + 751: resources_core.FormatString(config.ControlPlaneConfiguration), + 752: resources_core.FormatString(config.WorkerConfiguration), + 753: resources_core.FormatString(config.AccessConfiguration), + 755: resources_core.FormatString(config.ClusterConfiguration), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 150, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *K8sSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.AccessConfiguration.IsNull() || data.AccessConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "access_configuration is required.") + return + } + if data.ClusterConfiguration.IsNull() || data.ClusterConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "cluster_configuration is required.") + return + } + if data.ControlPlaneConfiguration.IsNull() || data.ControlPlaneConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "control_plane_configuration is required.") + return + } + if data.StartupConfiguration.IsNull() || data.StartupConfiguration.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 750: resources_core.FormatString(data.StartupConfiguration), + 751: resources_core.FormatString(data.ControlPlaneConfiguration), + 752: resources_core.FormatString(data.WorkerConfiguration), + 753: resources_core.FormatString(data.AccessConfiguration), + 755: resources_core.FormatString(data.ClusterConfiguration), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 150, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "string"}, + {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "string"}, + {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "string"}, + {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "string"}, + {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 150, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "string"}, + {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "string"}, + {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "string"}, + {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "string"}, + {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sSthutrvalClusterModel + var state K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ClusterConfiguration.IsNull() != state.ClusterConfiguration.IsNull() || plan.ClusterConfiguration.IsUnknown() != state.ClusterConfiguration.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ClusterConfiguration.IsNull() && !plan.ClusterConfiguration.IsUnknown() && !state.ClusterConfiguration.IsNull() && !state.ClusterConfiguration.IsUnknown() { + if plan.ClusterConfiguration.ValueString() != state.ClusterConfiguration.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ControlPlaneConfiguration.IsNull() != state.ControlPlaneConfiguration.IsNull() || plan.ControlPlaneConfiguration.IsUnknown() != state.ControlPlaneConfiguration.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ControlPlaneConfiguration.IsNull() && !plan.ControlPlaneConfiguration.IsUnknown() && !state.ControlPlaneConfiguration.IsNull() && !state.ControlPlaneConfiguration.IsUnknown() { + if plan.ControlPlaneConfiguration.ValueString() != state.ControlPlaneConfiguration.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.WorkerConfiguration.IsNull() != state.WorkerConfiguration.IsNull() || plan.WorkerConfiguration.IsUnknown() != state.WorkerConfiguration.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { + if plan.WorkerConfiguration.ValueString() != state.WorkerConfiguration.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessConfiguration.IsNull() != state.AccessConfiguration.IsNull() || plan.AccessConfiguration.IsUnknown() != state.AccessConfiguration.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessConfiguration.IsNull() && !plan.AccessConfiguration.IsUnknown() && !state.AccessConfiguration.IsNull() && !state.AccessConfiguration.IsUnknown() { + if plan.AccessConfiguration.ValueString() != state.AccessConfiguration.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 756: resources_core.FormatString(plan.ClusterConfiguration), + 757: resources_core.FormatString(plan.ControlPlaneConfiguration), + 758: resources_core.FormatString(plan.WorkerConfiguration), + 759: resources_core.FormatString(plan.AccessConfiguration), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 150, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "string"}, + {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "string"}, + {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "string"}, + {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "string"}, + {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *K8sSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *K8sSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/111_dnsrecord_reconcile_action.go b/generated/dev/go/150_k8s_sthutrval_cluster_update_secrets_action.go similarity index 61% rename from provider/internal/resources_gen/111_dnsrecord_reconcile_action.go rename to generated/dev/go/150_k8s_sthutrval_cluster_update_secrets_action.go index ef6e7ab..b019fdb 100644 --- a/provider/internal/resources_gen/111_dnsrecord_reconcile_action.go +++ b/generated/dev/go/150_k8s_sthutrval_cluster_update_secrets_action.go @@ -14,37 +14,37 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnsrecord -// Action: reconcile +// Service: k8s_sthutrval_cluster +// Action: update_secrets -var _ resource.Resource = &DnsrecordReconcileResource{} +var _ resource.Resource = &K8sSthutrvalClusterUpdateSecretsResource{} -// DnsrecordReconcileResource triggers a one-time action. -type DnsrecordReconcileResource struct { +// K8sSthutrvalClusterUpdateSecretsResource triggers a one-time action. +type K8sSthutrvalClusterUpdateSecretsResource struct { client *core.UniversalClient } -type DnsrecordReconcileModel struct { +type K8sSthutrvalClusterUpdateSecretsModel struct { ID types.String `tfsdk:"id"` - DnsrecordID types.String `tfsdk:"dnsrecord_id"` + K8sSthutrvalClusterID types.String `tfsdk:"k8s_sthutrval_cluster_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` LogLevel types.String `tfsdk:"log_level"` } -func NewDnsrecordReconcileResource() resource.Resource { - return &DnsrecordReconcileResource{} +func NewK8sSthutrvalClusterUpdateSecretsResource() resource.Resource { + return &K8sSthutrvalClusterUpdateSecretsResource{} } -func (r *DnsrecordReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnsrecord_reconcile" +func (r *K8sSthutrvalClusterUpdateSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster_update_secrets" } -func (r *DnsrecordReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *K8sSthutrvalClusterUpdateSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "dnsrecord_id": schema.StringAttribute{Required: true}, + "k8s_sthutrval_cluster_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, @@ -53,14 +53,14 @@ func (r *DnsrecordReconcileResource) Schema(ctx context.Context, req resource.Sc resp.Schema = schema.Schema{Attributes: attrs} } -func (r *DnsrecordReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan DnsrecordReconcileModel +func (r *K8sSthutrvalClusterUpdateSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sSthutrvalClusterUpdateSecretsModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.DnsrecordID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -76,17 +76,17 @@ func (r *DnsrecordReconcileResource) Create(ctx context.Context, req resource.Cr if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "update_secrets", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "update_secrets", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *DnsrecordReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnsrecordReconcileModel +func (r *K8sSthutrvalClusterUpdateSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sSthutrvalClusterUpdateSecretsModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +94,14 @@ func (r *DnsrecordReconcileResource) Read(ctx context.Context, req resource.Read resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *DnsrecordReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnsrecordReconcileModel +func (r *K8sSthutrvalClusterUpdateSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sSthutrvalClusterUpdateSecretsModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.DnsrecordID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -110,20 +110,20 @@ func (r *DnsrecordReconcileResource) Update(ctx context.Context, req resource.Up params := map[string]string{} - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "update_secrets", params); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "update_secrets", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *DnsrecordReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *K8sSthutrvalClusterUpdateSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *DnsrecordReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *K8sSthutrvalClusterUpdateSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/dev/go/151_k8s_openbao_kv_resource.go b/generated/dev/go/151_k8s_openbao_kv_resource.go new file mode 100644 index 0000000..7e46c20 --- /dev/null +++ b/generated/dev/go/151_k8s_openbao_kv_resource.go @@ -0,0 +1,347 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_openbao +// Subresource: kv + +var _ resource.Resource = &K8sOpenbaoKvResource{} + +// K8sOpenbaoKvResource manages a subresource via operations. +type K8sOpenbaoKvResource struct { + client *core.UniversalClient +} + +type K8sOpenbaoKvModel struct { + ID types.String `tfsdk:"id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + KvName types.String `tfsdk:"kv_name"` +} + +func NewK8sOpenbaoKvResource() resource.Resource { + return &K8sOpenbaoKvResource{} +} + +func (r *K8sOpenbaoKvResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_kv" +} + +func (r *K8sOpenbaoKvResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "k8s_openbao_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "kv_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("kvname-"), MarkdownDescription: "Будет создан `v2 тип движка`

Уникальное в рамках услуги Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sOpenbaoKvResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoKvModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + } + identityCodes := []string{ + "kvName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoKvResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoKvModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoKvResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoKvModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state K8sOpenbaoKvModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoKvResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sOpenbaoKvModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_kv" == "" { + return + } + + instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "kvName": resources_core.FormatString(state.KvName), + } + identityCodes := []string{ + "kvName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "kvName": resources_core.FormatString(state.KvName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_kv", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *K8sOpenbaoKvResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/151_k8s_openbao_policy_resource.go b/generated/dev/go/151_k8s_openbao_policy_resource.go new file mode 100644 index 0000000..1c8e3c4 --- /dev/null +++ b/generated/dev/go/151_k8s_openbao_policy_resource.go @@ -0,0 +1,359 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_openbao +// Subresource: policy + +var _ resource.Resource = &K8sOpenbaoPolicyResource{} + +// K8sOpenbaoPolicyResource manages a subresource via operations. +type K8sOpenbaoPolicyResource struct { + client *core.UniversalClient +} + +type K8sOpenbaoPolicyModel struct { + ID types.String `tfsdk:"id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + PolicyName types.String `tfsdk:"policy_name"` + PolicySettings types.String `tfsdk:"policy_settings"` +} + +func NewK8sOpenbaoPolicyResource() resource.Resource { + return &K8sOpenbaoPolicyResource{} +} + +func (r *K8sOpenbaoPolicyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_policy" +} + +func (r *K8sOpenbaoPolicyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "k8s_openbao_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "policy_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("policy-"), MarkdownDescription: "Сделано для дальнейшего удобного использования Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "policy_settings": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sOpenbaoPolicyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoPolicyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.PolicySettings.IsNull() || plan.PolicySettings.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "policy_settings is required.") + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "policyName": resources_core.FormatString(plan.PolicyName), + "policySettings": resources_core.FormatString(plan.PolicySettings), + } + identityCodes := []string{ + "policyName", + "policySettings", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "policySettings": resources_core.FormatString(plan.PolicySettings), + "policyName": resources_core.FormatString(plan.PolicyName), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoPolicyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoPolicyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoPolicyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoPolicyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state K8sOpenbaoPolicyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "policyName": resources_core.FormatString(plan.PolicyName), + "policySettings": resources_core.FormatString(plan.PolicySettings), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoPolicyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sOpenbaoPolicyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_policy" == "" { + return + } + + instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "policyName": resources_core.FormatString(state.PolicyName), + "policySettings": resources_core.FormatString(state.PolicySettings), + } + identityCodes := []string{ + "policyName", + "policySettings", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "policyName": resources_core.FormatString(state.PolicyName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_policy", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *K8sOpenbaoPolicyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/120_clickhouse_reconcile_action.go b/generated/dev/go/151_k8s_openbao_reconcile_action.go similarity index 77% rename from provider/internal/resources_gen/120_clickhouse_reconcile_action.go rename to generated/dev/go/151_k8s_openbao_reconcile_action.go index 0688703..74109bb 100644 --- a/provider/internal/resources_gen/120_clickhouse_reconcile_action.go +++ b/generated/dev/go/151_k8s_openbao_reconcile_action.go @@ -14,37 +14,37 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse +// Service: k8s_openbao // Action: reconcile -var _ resource.Resource = &ClickhouseReconcileResource{} +var _ resource.Resource = &K8sOpenbaoReconcileResource{} -// ClickhouseReconcileResource triggers a one-time action. -type ClickhouseReconcileResource struct { +// K8sOpenbaoReconcileResource triggers a one-time action. +type K8sOpenbaoReconcileResource struct { client *core.UniversalClient } -type ClickhouseReconcileModel struct { +type K8sOpenbaoReconcileModel struct { ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` LogLevel types.String `tfsdk:"log_level"` } -func NewClickhouseReconcileResource() resource.Resource { - return &ClickhouseReconcileResource{} +func NewK8sOpenbaoReconcileResource() resource.Resource { + return &K8sOpenbaoReconcileResource{} } -func (r *ClickhouseReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_reconcile" +func (r *K8sOpenbaoReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_reconcile" } -func (r *ClickhouseReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *K8sOpenbaoReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "clickhouse_id": schema.StringAttribute{Required: true}, + "k8s_openbao_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, @@ -53,14 +53,14 @@ func (r *ClickhouseReconcileResource) Schema(ctx context.Context, req resource.S resp.Schema = schema.Schema{Attributes: attrs} } -func (r *ClickhouseReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseReconcileModel +func (r *K8sOpenbaoReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -85,8 +85,8 @@ func (r *ClickhouseReconcileResource) Create(ctx context.Context, req resource.C resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *ClickhouseReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseReconcileModel +func (r *K8sOpenbaoReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoReconcileModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +94,14 @@ func (r *ClickhouseReconcileResource) Read(ctx context.Context, req resource.Rea resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *ClickhouseReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseReconcileModel +func (r *K8sOpenbaoReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -119,11 +119,11 @@ func (r *ClickhouseReconcileResource) Update(ctx context.Context, req resource.U resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *ClickhouseReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *K8sOpenbaoReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *ClickhouseReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *K8sOpenbaoReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/dev/go/151_k8s_openbao_recovery_secrets_action.go b/generated/dev/go/151_k8s_openbao_recovery_secrets_action.go new file mode 100644 index 0000000..35d0782 --- /dev/null +++ b/generated/dev/go/151_k8s_openbao_recovery_secrets_action.go @@ -0,0 +1,162 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_openbao +// Action: recovery_secrets + +var _ resource.Resource = &K8sOpenbaoRecoverySecretsResource{} + +// K8sOpenbaoRecoverySecretsResource triggers a one-time action. +type K8sOpenbaoRecoverySecretsResource struct { + client *core.UniversalClient +} + +type K8sOpenbaoRecoverySecretsModel struct { + ID types.String `tfsdk:"id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + KvName types.String `tfsdk:"kv_name"` + SecretPath types.String `tfsdk:"secret_path"` + SecretVersion types.String `tfsdk:"secret_version"` +} + +func NewK8sOpenbaoRecoverySecretsResource() resource.Resource { + return &K8sOpenbaoRecoverySecretsResource{} +} + +func (r *K8sOpenbaoRecoverySecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_recovery_secrets" +} + +func (r *K8sOpenbaoRecoverySecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "k8s_openbao_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "kv_name": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка"}, + "secret_path": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка"}, + "secret_version": schema.StringAttribute{Required: true}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sOpenbaoRecoverySecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoRecoverySecretsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.KvName.IsNull() || plan.KvName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kv_name is required.") + return + } + if plan.SecretPath.IsNull() || plan.SecretPath.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "secret_path is required.") + return + } + if plan.SecretVersion.IsNull() || plan.SecretVersion.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "secret_version is required.") + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + "secretPath": resources_core.FormatString(plan.SecretPath), + "secretVersion": resources_core.FormatString(plan.SecretVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "recovery_secrets", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "recovery_secrets", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoRecoverySecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoRecoverySecretsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoRecoverySecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoRecoverySecretsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + "secretPath": resources_core.FormatString(plan.SecretPath), + "secretVersion": resources_core.FormatString(plan.SecretVersion), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "recovery_secrets", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "recovery_secrets", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoRecoverySecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *K8sOpenbaoRecoverySecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/151_k8s_openbao_resource.go b/generated/dev/go/151_k8s_openbao_resource.go new file mode 100644 index 0000000..c593cd6 --- /dev/null +++ b/generated/dev/go/151_k8s_openbao_resource.go @@ -0,0 +1,452 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_openbao +// Service ID: 151 + +var _ resource.Resource = &K8sOpenbaoResource{} +var _ resource.ResourceWithModifyPlan = &K8sOpenbaoResource{} +var _ resource.ResourceWithImportState = &K8sOpenbaoResource{} + +type K8sOpenbaoResource struct { + client *core.UniversalClient +} + +type K8sOpenbaoModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AppVersion types.String `tfsdk:"app_version"` + Domain types.String `tfsdk:"domain"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewK8sOpenbaoResource() resource.Resource { + return &K8sOpenbaoResource{} +} + +func (r *K8sOpenbaoResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao" +} + +func (r *K8sOpenbaoResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.4.4"), MarkdownDescription: "Выбрать из списка"}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("bao-"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `500`"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `10`"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3), MarkdownDescription: "Пример: `3`"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `2048`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sOpenbaoResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *K8sOpenbaoModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *K8sOpenbaoModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan K8sOpenbaoModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 663: resources_core.FormatString(config.ResourceRealm), + 664: resources_core.FormatInt64(config.ResourceCPU), + 665: resources_core.FormatInt64(config.ResourceInstances), + 666: resources_core.FormatInt64(config.ResourceMemory), + 667: resources_core.FormatInt64(config.ResourceDisk), + 668: resources_core.FormatString(config.AppVersion), + 669: resources_core.FormatString(config.Domain), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 151, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *K8sOpenbaoResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data K8sOpenbaoModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 663: resources_core.FormatString(data.ResourceRealm), + 664: resources_core.FormatInt64(data.ResourceCPU), + 665: resources_core.FormatInt64(data.ResourceInstances), + 666: resources_core.FormatInt64(data.ResourceMemory), + 667: resources_core.FormatInt64(data.ResourceDisk), + 668: resources_core.FormatString(data.AppVersion), + 669: resources_core.FormatString(data.Domain), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 151, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 151, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *K8sOpenbaoResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoModel + var state K8sOpenbaoModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 670: resources_core.FormatInt64(plan.ResourceInstances), + 671: resources_core.FormatInt64(plan.ResourceCPU), + 672: resources_core.FormatInt64(plan.ResourceMemory), + 673: resources_core.FormatInt64(plan.ResourceDisk), + 674: resources_core.FormatString(plan.AppVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 151, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sOpenbaoModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *K8sOpenbaoResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *K8sOpenbaoResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/2_template_reconcile_action.go b/generated/dev/go/151_k8s_openbao_restart_action.go similarity index 74% rename from provider/internal/resources_gen/2_template_reconcile_action.go rename to generated/dev/go/151_k8s_openbao_restart_action.go index 0d74ebe..b2f6d0e 100644 --- a/provider/internal/resources_gen/2_template_reconcile_action.go +++ b/generated/dev/go/151_k8s_openbao_restart_action.go @@ -14,37 +14,37 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: template -// Action: reconcile +// Service: k8s_openbao +// Action: restart -var _ resource.Resource = &TemplateReconcileResource{} +var _ resource.Resource = &K8sOpenbaoRestartResource{} -// TemplateReconcileResource triggers a one-time action. -type TemplateReconcileResource struct { +// K8sOpenbaoRestartResource triggers a one-time action. +type K8sOpenbaoRestartResource struct { client *core.UniversalClient } -type TemplateReconcileModel struct { +type K8sOpenbaoRestartModel struct { ID types.String `tfsdk:"id"` - TemplateID types.String `tfsdk:"template_id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` LogLevel types.String `tfsdk:"log_level"` } -func NewTemplateReconcileResource() resource.Resource { - return &TemplateReconcileResource{} +func NewK8sOpenbaoRestartResource() resource.Resource { + return &K8sOpenbaoRestartResource{} } -func (r *TemplateReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_template_reconcile" +func (r *K8sOpenbaoRestartResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_restart" } -func (r *TemplateReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *K8sOpenbaoRestartResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "template_id": schema.StringAttribute{Required: true}, + "k8s_openbao_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, @@ -53,14 +53,14 @@ func (r *TemplateReconcileResource) Schema(ctx context.Context, req resource.Sch resp.Schema = schema.Schema{Attributes: attrs} } -func (r *TemplateReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan TemplateReconcileModel +func (r *K8sOpenbaoRestartResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoRestartModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.TemplateID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -76,17 +76,17 @@ func (r *TemplateReconcileResource) Create(ctx context.Context, req resource.Cre if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "restart", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *TemplateReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TemplateReconcileModel +func (r *K8sOpenbaoRestartResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoRestartModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +94,14 @@ func (r *TemplateReconcileResource) Read(ctx context.Context, req resource.ReadR resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *TemplateReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TemplateReconcileModel +func (r *K8sOpenbaoRestartResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoRestartModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.TemplateID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -110,20 +110,20 @@ func (r *TemplateReconcileResource) Update(ctx context.Context, req resource.Upd params := map[string]string{} - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "restart", params); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *TemplateReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *K8sOpenbaoRestartResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *TemplateReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *K8sOpenbaoRestartResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/dev/go/151_k8s_openbao_secrets_resource.go b/generated/dev/go/151_k8s_openbao_secrets_resource.go new file mode 100644 index 0000000..2f499a3 --- /dev/null +++ b/generated/dev/go/151_k8s_openbao_secrets_resource.go @@ -0,0 +1,372 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_openbao +// Subresource: secrets + +var _ resource.Resource = &K8sOpenbaoSecretsResource{} + +// K8sOpenbaoSecretsResource manages a subresource via operations. +type K8sOpenbaoSecretsResource struct { + client *core.UniversalClient +} + +type K8sOpenbaoSecretsModel struct { + ID types.String `tfsdk:"id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + KvName types.String `tfsdk:"kv_name"` + SecretData types.String `tfsdk:"secret_data"` + SecretPath types.String `tfsdk:"secret_path"` +} + +func NewK8sOpenbaoSecretsResource() resource.Resource { + return &K8sOpenbaoSecretsResource{} +} + +func (r *K8sOpenbaoSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_secrets" +} + +func (r *K8sOpenbaoSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "k8s_openbao_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "kv_name": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "secret_data": schema.StringAttribute{Required: true, MarkdownDescription: "Хотя бы один секрет необходимо добавить Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "secret_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("path/to/mysecret"), MarkdownDescription: "Пример: `path` или `road/to/path` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sOpenbaoSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoSecretsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.KvName.IsNull() || plan.KvName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kv_name is required.") + return + } + if plan.SecretData.IsNull() || plan.SecretData.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "secret_data is required.") + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + "secretData": resources_core.FormatString(plan.SecretData), + "secretPath": resources_core.FormatString(plan.SecretPath), + } + identityCodes := []string{ + "kvName", + "secretData", + "secretPath", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + "secretPath": resources_core.FormatString(plan.SecretPath), + "secretData": resources_core.FormatString(plan.SecretData), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoSecretsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoSecretsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state K8sOpenbaoSecretsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + "secretData": resources_core.FormatString(plan.SecretData), + "secretPath": resources_core.FormatString(plan.SecretPath), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sOpenbaoSecretsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_secrets" == "" { + return + } + + instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "kvName": resources_core.FormatString(state.KvName), + "secretData": resources_core.FormatString(state.SecretData), + "secretPath": resources_core.FormatString(state.SecretPath), + } + identityCodes := []string{ + "kvName", + "secretData", + "secretPath", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "kvName": resources_core.FormatString(state.KvName), + "secretPath": resources_core.FormatString(state.SecretPath), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *K8sOpenbaoSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/151_k8s_openbao_update_policy_action.go b/generated/dev/go/151_k8s_openbao_update_policy_action.go new file mode 100644 index 0000000..e8c698f --- /dev/null +++ b/generated/dev/go/151_k8s_openbao_update_policy_action.go @@ -0,0 +1,154 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_openbao +// Action: update_policy + +var _ resource.Resource = &K8sOpenbaoUpdatePolicyResource{} + +// K8sOpenbaoUpdatePolicyResource triggers a one-time action. +type K8sOpenbaoUpdatePolicyResource struct { + client *core.UniversalClient +} + +type K8sOpenbaoUpdatePolicyModel struct { + ID types.String `tfsdk:"id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + PolicyName types.String `tfsdk:"policy_name"` + PolicySettings types.String `tfsdk:"policy_settings"` +} + +func NewK8sOpenbaoUpdatePolicyResource() resource.Resource { + return &K8sOpenbaoUpdatePolicyResource{} +} + +func (r *K8sOpenbaoUpdatePolicyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_update_policy" +} + +func (r *K8sOpenbaoUpdatePolicyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "k8s_openbao_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "policy_name": schema.StringAttribute{Required: true}, + "policy_settings": schema.StringAttribute{Required: true, MarkdownDescription: "Производится полная перезапись политики!"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sOpenbaoUpdatePolicyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoUpdatePolicyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.PolicyName.IsNull() || plan.PolicyName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "policy_name is required.") + return + } + if plan.PolicySettings.IsNull() || plan.PolicySettings.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "policy_settings is required.") + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "policyName": resources_core.FormatString(plan.PolicyName), + "policySettings": resources_core.FormatString(plan.PolicySettings), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "update_policy", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "update_policy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoUpdatePolicyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoUpdatePolicyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoUpdatePolicyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoUpdatePolicyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "policyName": resources_core.FormatString(plan.PolicyName), + "policySettings": resources_core.FormatString(plan.PolicySettings), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "update_policy", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "update_policy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoUpdatePolicyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *K8sOpenbaoUpdatePolicyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/151_k8s_openbao_update_secrets_action.go b/generated/dev/go/151_k8s_openbao_update_secrets_action.go new file mode 100644 index 0000000..66ace51 --- /dev/null +++ b/generated/dev/go/151_k8s_openbao_update_secrets_action.go @@ -0,0 +1,166 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_openbao +// Action: update_secrets + +var _ resource.Resource = &K8sOpenbaoUpdateSecretsResource{} + +// K8sOpenbaoUpdateSecretsResource triggers a one-time action. +type K8sOpenbaoUpdateSecretsResource struct { + client *core.UniversalClient +} + +type K8sOpenbaoUpdateSecretsModel struct { + ID types.String `tfsdk:"id"` + K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + KvName types.String `tfsdk:"kv_name"` + SecretPath types.String `tfsdk:"secret_path"` + NeedFullUpdateSecret types.Bool `tfsdk:"need_full_update_secret"` + SecretData types.String `tfsdk:"secret_data"` +} + +func NewK8sOpenbaoUpdateSecretsResource() resource.Resource { + return &K8sOpenbaoUpdateSecretsResource{} +} + +func (r *K8sOpenbaoUpdateSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_openbao_update_secrets" +} + +func (r *K8sOpenbaoUpdateSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "k8s_openbao_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "kv_name": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка"}, + "secret_path": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка"}, + "need_full_update_secret": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Если выбрать `true`, будет создана новая версия секрета `с нуля`
Если выбрать `false`, будет создана новая версия секрета с данными прошлой ревизии, а переданные данные в операции либо будут модифицированы, либо будут добавлены"}, + "secret_data": schema.StringAttribute{Required: true, MarkdownDescription: "Хотя бы один секрет необходимо добавить"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sOpenbaoUpdateSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sOpenbaoUpdateSecretsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.KvName.IsNull() || plan.KvName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kv_name is required.") + return + } + if plan.SecretPath.IsNull() || plan.SecretPath.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "secret_path is required.") + return + } + if plan.SecretData.IsNull() || plan.SecretData.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "secret_data is required.") + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + "secretPath": resources_core.FormatString(plan.SecretPath), + "needFullUpdateSecret": resources_core.FormatBool(plan.NeedFullUpdateSecret), + "secretData": resources_core.FormatString(plan.SecretData), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "update_secrets", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "update_secrets", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoUpdateSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sOpenbaoUpdateSecretsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sOpenbaoUpdateSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sOpenbaoUpdateSecretsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "kvName": resources_core.FormatString(plan.KvName), + "secretPath": resources_core.FormatString(plan.SecretPath), + "needFullUpdateSecret": resources_core.FormatBool(plan.NeedFullUpdateSecret), + "secretData": resources_core.FormatString(plan.SecretData), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "update_secrets", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "update_secrets", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *K8sOpenbaoUpdateSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *K8sOpenbaoUpdateSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/153_nifi_resource.go b/generated/dev/go/153_nifi_resource.go new file mode 100644 index 0000000..67a3c9c --- /dev/null +++ b/generated/dev/go/153_nifi_resource.go @@ -0,0 +1,449 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nifi +// Service ID: 153 + +var _ resource.Resource = &NifiResource{} +var _ resource.ResourceWithModifyPlan = &NifiResource{} +var _ resource.ResourceWithImportState = &NifiResource{} + +type NifiResource struct { + client *core.UniversalClient +} + +type NifiModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AppVersion types.String `tfsdk:"app_version"` + Domain types.String `tfsdk:"domain"` + JdbcDrivers types.String `tfsdk:"jdbc_drivers"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNifiResource() resource.Resource { + return &NifiResource{} +} + +func (r *NifiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nifi" +} + +func (r *NifiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.6.0"), MarkdownDescription: "Выбрать из списка Не изменяется после создания."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nifi-"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "jdbc_drivers": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `https://jdbc.postgresql.org/download/postgresql-42.7.8.jar` Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Пример: `2000` Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `10` Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1` Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(4096), MarkdownDescription: "Пример: `2048` Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NifiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NifiModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NifiModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NifiModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить app_version", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.JdbcDrivers.IsNull() && !plan.JdbcDrivers.IsUnknown() && !state.JdbcDrivers.IsNull() && !state.JdbcDrivers.IsUnknown() { + if plan.JdbcDrivers.ValueString() != state.JdbcDrivers.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить jdbc_drivers", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.JdbcDrivers.IsNull() || config.JdbcDrivers.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "jdbc_drivers is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 711: resources_core.FormatString(config.Domain), + 712: resources_core.FormatInt64(config.ResourceCPU), + 713: resources_core.FormatInt64(config.ResourceMemory), + 714: resources_core.FormatInt64(config.ResourceInstances), + 715: resources_core.FormatInt64(config.ResourceDisk), + 716: resources_core.FormatString(config.AppVersion), + 717: resources_core.FormatString(config.ResourceRealm), + 718: resources_core.FormatString(config.JdbcDrivers), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 153, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NifiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NifiModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.JdbcDrivers.IsNull() || data.JdbcDrivers.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "jdbc_drivers is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 153, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 711: resources_core.FormatString(data.Domain), + 712: resources_core.FormatInt64(data.ResourceCPU), + 713: resources_core.FormatInt64(data.ResourceMemory), + 714: resources_core.FormatInt64(data.ResourceInstances), + 715: resources_core.FormatInt64(data.ResourceDisk), + 716: resources_core.FormatString(data.AppVersion), + 717: resources_core.FormatString(data.ResourceRealm), + 718: resources_core.FormatString(data.JdbcDrivers), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 153, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 153, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NifiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NifiModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 153, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NifiModel + var state NifiModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 153, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NifiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NifiModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NifiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NifiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/19_vc_org_reconcile_action.go b/generated/dev/go/19_vc_org_reconcile_action.go similarity index 100% rename from provider/internal/resources_gen/19_vc_org_reconcile_action.go rename to generated/dev/go/19_vc_org_reconcile_action.go diff --git a/generated/dev/go/19_vc_org_resource.go b/generated/dev/go/19_vc_org_resource.go new file mode 100644 index 0000000..d004f8c --- /dev/null +++ b/generated/dev/go/19_vc_org_resource.go @@ -0,0 +1,375 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_org +// Service ID: 19 + +var _ resource.Resource = &VcOrgResource{} +var _ resource.ResourceWithModifyPlan = &VcOrgResource{} +var _ resource.ResourceWithImportState = &VcOrgResource{} + +type VcOrgResource struct { + client *core.UniversalClient +} + +type VcOrgModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + OrganizationType types.String `tfsdk:"organization_type"` + ResourceRealm types.String `tfsdk:"resource_realm"` + VIPConfigure types.String `tfsdk:"v_i_p_configure"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcOrgResource() resource.Resource { + return &VcOrgResource{} +} + +func (r *VcOrgResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_org" +} + +func (r *VcOrgResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "organization_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("iaas"), MarkdownDescription: "Доступ к организации предоставляется только при выборе `iaas` Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Облако в котором будет развёрнут тенант Не изменяется после создания."}, + "v_i_p_configure": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]")}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcOrgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcOrgModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcOrgModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcOrgModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.OrganizationType.IsNull() && !plan.OrganizationType.IsUnknown() && !state.OrganizationType.IsNull() && !state.OrganizationType.IsUnknown() { + if plan.OrganizationType.ValueString() != state.OrganizationType.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить organization_type", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 418: resources_core.FormatString(config.ResourceRealm), + 556: resources_core.FormatString(config.OrganizationType), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 19, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcOrgResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcOrgModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 418: resources_core.FormatString(data.ResourceRealm), + 556: resources_core.FormatString(data.OrganizationType), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 19, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "organizationType", Field: "OrganizationType", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "vIPConfigure", Field: "VIPConfigure", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcOrgResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcOrgModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 19, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "organizationType", Field: "OrganizationType", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "vIPConfigure", Field: "VIPConfigure", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcOrgModel + var state VcOrgModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.VIPConfigure.IsNull() != state.VIPConfigure.IsNull() || plan.VIPConfigure.IsUnknown() != state.VIPConfigure.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VIPConfigure.IsNull() && !plan.VIPConfigure.IsUnknown() && !state.VIPConfigure.IsNull() && !state.VIPConfigure.IsUnknown() { + if plan.VIPConfigure.ValueString() != state.VIPConfigure.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 662: resources_core.FormatString(plan.VIPConfigure), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 19, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "organizationType", Field: "OrganizationType", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "vIPConfigure", Field: "VIPConfigure", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcOrgResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcOrgModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcOrgResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcOrgResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/1_dummy_reconcile_action.go b/generated/dev/go/1_dummy_reconcile_action.go similarity index 100% rename from provider/internal/resources_gen/1_dummy_reconcile_action.go rename to generated/dev/go/1_dummy_reconcile_action.go diff --git a/provider/internal/resources_gen/1_dummy_redeploy_action.go b/generated/dev/go/1_dummy_redeploy_action.go similarity index 100% rename from provider/internal/resources_gen/1_dummy_redeploy_action.go rename to generated/dev/go/1_dummy_redeploy_action.go diff --git a/provider/internal/resources_gen/1_dummy_resource.go b/generated/dev/go/1_dummy_resource.go similarity index 85% rename from provider/internal/resources_gen/1_dummy_resource.go rename to generated/dev/go/1_dummy_resource.go index b2b4104..bb2e7ac 100644 --- a/provider/internal/resources_gen/1_dummy_resource.go +++ b/generated/dev/go/1_dummy_resource.go @@ -2,7 +2,6 @@ package resources_gen import ( "context" - "strings" "terraform-provider-nubes/internal/core" "terraform-provider-nubes/internal/resources_core" @@ -13,7 +12,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -34,7 +32,6 @@ type DummyModel struct { ResourceName types.String `tfsdk:"resource_name"` OperationTimeout types.String `tfsdk:"operation_timeout"` LogLevel types.String `tfsdk:"log_level"` - Arr types.String `tfsdk:"arr"` ArrayMapFixedExample types.String `tfsdk:"array_map_fixed_example"` Bodymessage types.String `tfsdk:"bodymessage"` DurationMs types.Int64 `tfsdk:"duration_ms"` @@ -46,6 +43,7 @@ type DummyModel struct { NestedRefExample types.String `tfsdk:"nested_ref_example"` ResourceRealm types.String `tfsdk:"resource_realm"` WhereFail types.Int64 `tfsdk:"where_fail"` + YamlExample types.String `tfsdk:"yaml_example"` SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` StateParams types.Map `tfsdk:"state_params"` @@ -68,22 +66,22 @@ func (r *DummyResource) Metadata(ctx context.Context, req resource.MetadataReque func (r *DummyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "id": schema.StringAttribute{Computed: true}, "resource_name": schema.StringAttribute{Required: true}, "operation_timeout": schema.StringAttribute{Optional: true}, "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "arr": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, "array_map_fixed_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Array of map-fixed Не изменяется после создания."}, "bodymessage": schema.StringAttribute{Optional: true, MarkdownDescription: "Дата, уходящая в Vault"}, "duration_ms": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "перед завершением подождать (sleep) столько миллисекунд"}, "fail_at_start": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, завершить джоб, ничего не записывая в cmdb"}, "fail_in_progress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb"}, - "json_example": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для тестирования интерфейса", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "json_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Для тестирования интерфейса", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, "map_example": schema.StringAttribute{Optional: true}, "map_fixed": schema.StringAttribute{Optional: true, MarkdownDescription: "Новый тип поля Не изменяется после создания."}, "nested_ref_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, "where_fail": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write"}, + "yaml_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, @@ -134,67 +132,42 @@ func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR return } } - if !plan.Arr.IsNull() && !plan.Arr.IsUnknown() && !state.Arr.IsNull() && !state.Arr.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Arr.ValueString(), state.Arr.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить arr", "Параметр задается при создании и не может быть изменен.") - return - } - } if !plan.ArrayMapFixedExample.IsNull() && !plan.ArrayMapFixedExample.IsUnknown() && !state.ArrayMapFixedExample.IsNull() && !state.ArrayMapFixedExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ArrayMapFixedExample.ValueString(), state.ArrayMapFixedExample.ValueString()) { + if plan.ArrayMapFixedExample.ValueString() != state.ArrayMapFixedExample.ValueString() { resp.Diagnostics.AddError("Нельзя изменить array_map_fixed_example", "Параметр задается при создании и не может быть изменен.") return } } if !plan.MapFixed.IsNull() && !plan.MapFixed.IsUnknown() && !state.MapFixed.IsNull() && !state.MapFixed.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.MapFixed.ValueString(), state.MapFixed.ValueString()) { + if plan.MapFixed.ValueString() != state.MapFixed.ValueString() { resp.Diagnostics.AddError("Нельзя изменить map_fixed", "Параметр задается при создании и не может быть изменен.") return } } if !plan.NestedRefExample.IsNull() && !plan.NestedRefExample.IsUnknown() && !state.NestedRefExample.IsNull() && !state.NestedRefExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NestedRefExample.ValueString(), state.NestedRefExample.ValueString()) { + if plan.NestedRefExample.ValueString() != state.NestedRefExample.ValueString() { resp.Diagnostics.AddError("Нельзя изменить nested_ref_example", "Параметр задается при создании и не может быть изменен.") return } } if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") return } } + if !plan.YamlExample.IsNull() && !plan.YamlExample.IsUnknown() && !state.YamlExample.IsNull() && !state.YamlExample.IsUnknown() { + if plan.YamlExample.ValueString() != state.YamlExample.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить yaml_example", "Параметр задается при создании и не может быть изменен.") + return + } + } return } - if config.Arr.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - if config.Arr.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return } - if config.ResourceRealm.IsUnknown() { - return - } if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { return @@ -213,12 +186,12 @@ func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanR 321: resources_core.FormatString(config.MapExample), 322: resources_core.FormatString(config.JsonExample), 396: resources_core.FormatString(config.NestedRefExample), + 447: resources_core.FormatString(config.YamlExample), 647: resources_core.FormatString(config.MapFixed), 654: resources_core.FormatString(config.ArrayMapFixedExample), - 863: resources_core.FormatString(config.Arr), } desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 1, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) } @@ -229,10 +202,6 @@ func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, if resp.Diagnostics.HasError() { return } - if data.Arr.IsNull() || data.Arr.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return @@ -240,7 +209,7 @@ func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, resourceName := data.ResourceName.ValueString() desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) params := map[int]string{ @@ -253,9 +222,9 @@ func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, 321: resources_core.FormatString(data.MapExample), 322: resources_core.FormatString(data.JsonExample), 396: resources_core.FormatString(data.NestedRefExample), + 447: resources_core.FormatString(data.YamlExample), 647: resources_core.FormatString(data.MapFixed), 654: resources_core.FormatString(data.ArrayMapFixedExample), - 863: resources_core.FormatString(data.Arr), } operationTimeout := "" @@ -282,7 +251,6 @@ func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, {Code: "vault_user_path"}, {Code: "vault_fields"}, }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, {Code: "durationMs", Field: "DurationMs", Type: "int64"}, @@ -294,6 +262,7 @@ func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + {Code: "yamlExample", Field: "YamlExample", Type: "string"}, }) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -334,7 +303,6 @@ func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp {Code: "vault_user_path"}, {Code: "vault_fields"}, }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, {Code: "durationMs", Field: "DurationMs", Type: "int64"}, @@ -346,6 +314,7 @@ func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + {Code: "yamlExample", Field: "YamlExample", Type: "string"}, }) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -414,7 +383,7 @@ func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, if plan.Bodymessage.IsNull() != state.Bodymessage.IsNull() || plan.Bodymessage.IsUnknown() != state.Bodymessage.IsUnknown() { hasServiceParamChanges = true } else if !plan.Bodymessage.IsNull() && !plan.Bodymessage.IsUnknown() && !state.Bodymessage.IsNull() && !state.Bodymessage.IsUnknown() { - if !strings.EqualFold(plan.Bodymessage.ValueString(), state.Bodymessage.ValueString()) { + if plan.Bodymessage.ValueString() != state.Bodymessage.ValueString() { hasServiceParamChanges = true } } @@ -423,7 +392,7 @@ func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, if plan.MapExample.IsNull() != state.MapExample.IsNull() || plan.MapExample.IsUnknown() != state.MapExample.IsUnknown() { hasServiceParamChanges = true } else if !plan.MapExample.IsNull() && !plan.MapExample.IsUnknown() && !state.MapExample.IsNull() && !state.MapExample.IsUnknown() { - if !strings.EqualFold(plan.MapExample.ValueString(), state.MapExample.ValueString()) { + if plan.MapExample.ValueString() != state.MapExample.ValueString() { hasServiceParamChanges = true } } @@ -432,7 +401,7 @@ func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, if plan.JsonExample.IsNull() != state.JsonExample.IsNull() || plan.JsonExample.IsUnknown() != state.JsonExample.IsUnknown() { hasServiceParamChanges = true } else if !plan.JsonExample.IsNull() && !plan.JsonExample.IsUnknown() && !state.JsonExample.IsNull() && !state.JsonExample.IsUnknown() { - if !strings.EqualFold(plan.JsonExample.ValueString(), state.JsonExample.ValueString()) { + if plan.JsonExample.ValueString() != state.JsonExample.ValueString() { hasServiceParamChanges = true } } @@ -485,7 +454,6 @@ func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, {Code: "vault_user_path"}, {Code: "vault_fields"}, }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, {Code: "durationMs", Field: "DurationMs", Type: "int64"}, @@ -497,6 +465,7 @@ func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + {Code: "yamlExample", Field: "YamlExample", Type: "string"}, }) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/provider/internal/resources_gen/21_vc_vdc_reconcile_action.go b/generated/dev/go/21_vc_vdc_reconcile_action.go similarity index 100% rename from provider/internal/resources_gen/21_vc_vdc_reconcile_action.go rename to generated/dev/go/21_vc_vdc_reconcile_action.go diff --git a/generated/dev/go/21_vc_vdc_resource.go b/generated/dev/go/21_vc_vdc_resource.go new file mode 100644 index 0000000..d3aff33 --- /dev/null +++ b/generated/dev/go/21_vc_vdc_resource.go @@ -0,0 +1,512 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vdc +// Service ID: 21 + +var _ resource.Resource = &VcVdcResource{} +var _ resource.ResourceWithModifyPlan = &VcVdcResource{} +var _ resource.ResourceWithImportState = &VcVdcResource{} + +type VcVdcResource struct { + client *core.UniversalClient +} + +type VcVdcModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + CpuAllocated types.Int64 `tfsdk:"cpu_allocated"` + CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` + MemAllocated types.Int64 `tfsdk:"mem_allocated"` + NetworkProvider types.String `tfsdk:"network_provider"` + OrganizationUid types.String `tfsdk:"organization_uid"` + ProviderVdc types.String `tfsdk:"provider_vdc"` + StorageConfig types.String `tfsdk:"storage_config"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVdcResource() resource.Resource { + return &VcVdcResource{} +} + +func (r *VcVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc" +} + +func (r *VcVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "cpu_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "шт"}, + "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "% резервирования Не изменяется после создания."}, + "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "gb"}, + "network_provider": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "organization_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` Не изменяется после создания."}, + "provider_vdc": schema.StringAttribute{Required: true, MarkdownDescription: "Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
Не изменяется после создания."}, + "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Первая политика в списке указывается как "По умолчанию""}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVdcResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVdcModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, plan.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != plan.OrganizationUid.ValueString() { + plan.OrganizationUid = types.StringValue(resolvedOrganizationUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { + if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NetworkProvider.IsNull() && !plan.NetworkProvider.IsUnknown() && !state.NetworkProvider.IsNull() && !state.NetworkProvider.IsUnknown() { + if plan.NetworkProvider.ValueString() != state.NetworkProvider.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить network_provider", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { + if plan.OrganizationUid.ValueString() != state.OrganizationUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить organization_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ProviderVdc.IsNull() && !plan.ProviderVdc.IsUnknown() && !state.ProviderVdc.IsNull() && !state.ProviderVdc.IsUnknown() { + if plan.ProviderVdc.ValueString() != state.ProviderVdc.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить provider_vdc", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.NetworkProvider.IsNull() || config.NetworkProvider.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") + return + } + if config.OrganizationUid.IsNull() || config.OrganizationUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") + return + } + if config.ProviderVdc.IsNull() || config.ProviderVdc.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.OrganizationUid.IsNull() && !config.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, config.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != config.OrganizationUid.ValueString() { + config.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + params := map[int]string{ + 30: resources_core.FormatString(config.OrganizationUid), + 335: resources_core.FormatString(config.ProviderVdc), + 361: resources_core.FormatString(config.StorageConfig), + 397: resources_core.FormatInt64(config.CpuGuaranteed), + 557: resources_core.FormatInt64(config.CpuAllocated), + 558: resources_core.FormatInt64(config.MemAllocated), + 746: resources_core.FormatString(config.NetworkProvider), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 21, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.NetworkProvider.IsNull() || data.NetworkProvider.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") + return + } + if data.OrganizationUid.IsNull() || data.OrganizationUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") + return + } + if data.ProviderVdc.IsNull() || data.ProviderVdc.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") + return + } + if !data.OrganizationUid.IsNull() && !data.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, data.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedOrganizationUid != "" && resolvedOrganizationUid != data.OrganizationUid.ValueString() { + data.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 30: resources_core.FormatString(data.OrganizationUid), + 335: resources_core.FormatString(data.ProviderVdc), + 361: resources_core.FormatString(data.StorageConfig), + 397: resources_core.FormatInt64(data.CpuGuaranteed), + 557: resources_core.FormatInt64(data.CpuAllocated), + 558: resources_core.FormatInt64(data.MemAllocated), + 746: resources_core.FormatString(data.NetworkProvider), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 21, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, + {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, + {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { + state.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 21, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, + {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, + {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, newState.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != newState.OrganizationUid.ValueString() { + newState.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcModel + var state VcVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.CpuAllocated.IsNull() != state.CpuAllocated.IsNull() || plan.CpuAllocated.IsUnknown() != state.CpuAllocated.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { + if plan.CpuAllocated.ValueInt64() != state.CpuAllocated.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MemAllocated.IsNull() != state.MemAllocated.IsNull() || plan.MemAllocated.IsUnknown() != state.MemAllocated.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { + if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.StorageConfig.IsNull() != state.StorageConfig.IsNull() || plan.StorageConfig.IsUnknown() != state.StorageConfig.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { + if plan.StorageConfig.ValueString() != state.StorageConfig.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 560: resources_core.FormatInt64(plan.CpuAllocated), + 561: resources_core.FormatInt64(plan.MemAllocated), + 562: resources_core.FormatString(plan.StorageConfig), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 21, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, + {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, + {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { + state.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVdcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/22_vc_nsxt_reconcile_action.go b/generated/dev/go/22_vc_nsxt_reconcile_action.go similarity index 100% rename from provider/internal/resources_gen/22_vc_nsxt_reconcile_action.go rename to generated/dev/go/22_vc_nsxt_reconcile_action.go diff --git a/generated/dev/go/22_vc_nsxt_resource.go b/generated/dev/go/22_vc_nsxt_resource.go new file mode 100644 index 0000000..1e4add5 --- /dev/null +++ b/generated/dev/go/22_vc_nsxt_resource.go @@ -0,0 +1,536 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_nsxt +// Service ID: 22 + +var _ resource.Resource = &VcNsxtResource{} +var _ resource.ResourceWithModifyPlan = &VcNsxtResource{} +var _ resource.ResourceWithImportState = &VcNsxtResource{} + +type VcNsxtResource struct { + client *core.UniversalClient +} + +type VcNsxtModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` + NeedExternalAddressSNAT types.Bool `tfsdk:"need_external_address_s_n_a_t"` + VdcGroupUid types.String `tfsdk:"vdc_group_uid"` + VdcType types.String `tfsdk:"vdc_type"` + VdcUid types.String `tfsdk:"vdc_uid"` + VirtualServicesCount types.Int64 `tfsdk:"virtual_services_count"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcNsxtResource() resource.Resource { + return &VcNsxtResource{} +} + +func (r *VcNsxtResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_nsxt" +} + +func (r *VcNsxtResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ip Space для внешнего IP"}, + "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включение Load Balancer"}, + "need_external_address_s_n_a_t": schema.BoolAttribute{Optional: true, MarkdownDescription: "Создать правило SNAT на подсеть `10.10.102.0/24`"}, + "vdc_group_uid": schema.StringAttribute{Optional: true, MarkdownDescription: "UUID Услуги `Группа виртуальных датацентров (groupvDC)` Не изменяется после создания."}, + "vdc_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vdc"), MarkdownDescription: "Тип родительской услуги Не изменяется после создания."}, + "vdc_uid": schema.StringAttribute{Optional: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, + "virtual_services_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Кол-во виртуальных сервисов, которые **резервируются** на AVI"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcNsxtResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcNsxtModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcNsxtModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcNsxtModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, plan.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != plan.VdcGroupUid.ValueString() { + plan.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + planChanged = true + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { + if plan.VdcGroupUid.ValueString() != state.VdcGroupUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_group_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcType.IsNull() && !plan.VdcType.IsUnknown() && !state.VdcType.IsNull() && !state.VdcType.IsUnknown() { + if plan.VdcType.ValueString() != state.VdcType.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_type", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.VdcGroupUid.IsNull() && !config.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, config.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != config.VdcGroupUid.ValueString() { + config.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + params := map[int]string{ + 8: resources_core.FormatString(config.VdcUid), + 340: resources_core.FormatBool(config.NeedEnableAVI), + 341: resources_core.FormatInt64(config.VirtualServicesCount), + 621: resources_core.FormatString(config.VdcType), + 622: resources_core.FormatString(config.VdcGroupUid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 22, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcNsxtResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcNsxtModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if !data.VdcGroupUid.IsNull() && !data.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, data.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != data.VdcGroupUid.ValueString() { + data.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 8: resources_core.FormatString(data.VdcUid), + 340: resources_core.FormatBool(data.NeedEnableAVI), + 341: resources_core.FormatInt64(data.VirtualServicesCount), + 621: resources_core.FormatString(data.VdcType), + 622: resources_core.FormatString(data.VdcGroupUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 22, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, + {Code: "vdcType", Field: "VdcType", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { + state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcNsxtResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcNsxtModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 22, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, + {Code: "vdcType", Field: "VdcType", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, newState.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != newState.VdcGroupUid.ValueString() { + newState.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcNsxtModel + var state VcNsxtModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.NeedEnableAVI.IsNull() != state.NeedEnableAVI.IsNull() || plan.NeedEnableAVI.IsUnknown() != state.NeedEnableAVI.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { + if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VirtualServicesCount.IsNull() != state.VirtualServicesCount.IsNull() || plan.VirtualServicesCount.IsUnknown() != state.VirtualServicesCount.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { + if plan.VirtualServicesCount.ValueInt64() != state.VirtualServicesCount.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressSNAT.IsNull() != state.NeedExternalAddressSNAT.IsNull() || plan.NeedExternalAddressSNAT.IsUnknown() != state.NeedExternalAddressSNAT.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressSNAT.IsNull() && !plan.NeedExternalAddressSNAT.IsUnknown() && !state.NeedExternalAddressSNAT.IsNull() && !state.NeedExternalAddressSNAT.IsUnknown() { + if plan.NeedExternalAddressSNAT.ValueBool() != state.NeedExternalAddressSNAT.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 368: resources_core.FormatBool(plan.NeedEnableAVI), + 369: resources_core.FormatInt64(plan.VirtualServicesCount), + 371: resources_core.FormatBool(plan.NeedExternalAddressSNAT), + 372: resources_core.FormatString(plan.IpSpaceName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 22, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, + {Code: "vdcType", Field: "VdcType", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { + state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcNsxtResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcNsxtModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcNsxtResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcNsxtResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/25_vcexternalip_resource.go b/generated/dev/go/25_vcexternalip_resource.go new file mode 100644 index 0000000..d068b24 --- /dev/null +++ b/generated/dev/go/25_vcexternalip_resource.go @@ -0,0 +1,569 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vcexternalip +// Service ID: 25 + +var _ resource.Resource = &VcexternalipResource{} +var _ resource.ResourceWithModifyPlan = &VcexternalipResource{} +var _ resource.ResourceWithImportState = &VcexternalipResource{} + +type VcexternalipResource struct { + client *core.UniversalClient +} + +type VcexternalipModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + DnatCreate types.Bool `tfsdk:"dnat_create"` + FromServiceCloudEdgeName types.String `tfsdk:"from_service_cloud_edge_name"` + FromServiceCloudEdgeScope types.String `tfsdk:"from_service_cloud_edge_scope"` + FromServiceCloudOrgName types.String `tfsdk:"from_service_cloud_org_name"` + FromServiceCloudVdcName types.String `tfsdk:"from_service_cloud_vdc_name"` + FromServiceCloudVmwareUrl types.String `tfsdk:"from_service_cloud_vmware_url"` + FromServiceNamespace types.String `tfsdk:"from_service_namespace"` + FromServiceVdcGroupName types.String `tfsdk:"from_service_vdc_group_name"` + InternalAddrAccess types.String `tfsdk:"internal_addr_access"` + InternalPortAccess types.String `tfsdk:"internal_port_access"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + ResourceRealm types.String `tfsdk:"resource_realm"` + ServiceUid types.String `tfsdk:"service_uid"` + SnatCreate types.Bool `tfsdk:"snat_create"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcexternalipResource() resource.Resource { + return &VcexternalipResource{} +} + +func (r *VcexternalipResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vcexternalip" +} + +func (r *VcexternalipResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "dnat_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходимо создать DNAT-правило."}, + "from_service_cloud_edge_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_cloud_edge_scope": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, + "from_service_cloud_org_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Org составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_cloud_vdc_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_cloud_vmware_url": schema.StringAttribute{Optional: true, MarkdownDescription: "Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_namespace": schema.StringAttribute{Required: true, MarkdownDescription: "Namespace составной услуги (для отображения правил FW) Не изменяется после создания."}, + "from_service_vdc_group_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, + "internal_addr_access": schema.StringAttribute{Required: true, MarkdownDescription: "Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true"}, + "internal_port_access": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5432"), MarkdownDescription: "Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
"}, + "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "service_uid": schema.StringAttribute{Required: true}, + "snat_create": schema.BoolAttribute{Optional: true, MarkdownDescription: "Необходимо создать SNAT-правило.
"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcexternalipResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcexternalipModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcexternalipModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcexternalipModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.FromServiceCloudEdgeName.IsNull() && !plan.FromServiceCloudEdgeName.IsUnknown() && !state.FromServiceCloudEdgeName.IsNull() && !state.FromServiceCloudEdgeName.IsUnknown() { + if plan.FromServiceCloudEdgeName.ValueString() != state.FromServiceCloudEdgeName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudEdgeScope.IsNull() && !plan.FromServiceCloudEdgeScope.IsUnknown() && !state.FromServiceCloudEdgeScope.IsNull() && !state.FromServiceCloudEdgeScope.IsUnknown() { + if plan.FromServiceCloudEdgeScope.ValueString() != state.FromServiceCloudEdgeScope.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_scope", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudOrgName.IsNull() && !plan.FromServiceCloudOrgName.IsUnknown() && !state.FromServiceCloudOrgName.IsNull() && !state.FromServiceCloudOrgName.IsUnknown() { + if plan.FromServiceCloudOrgName.ValueString() != state.FromServiceCloudOrgName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_org_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudVdcName.IsNull() && !plan.FromServiceCloudVdcName.IsUnknown() && !state.FromServiceCloudVdcName.IsNull() && !state.FromServiceCloudVdcName.IsUnknown() { + if plan.FromServiceCloudVdcName.ValueString() != state.FromServiceCloudVdcName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vdc_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudVmwareUrl.IsNull() && !plan.FromServiceCloudVmwareUrl.IsUnknown() && !state.FromServiceCloudVmwareUrl.IsNull() && !state.FromServiceCloudVmwareUrl.IsUnknown() { + if plan.FromServiceCloudVmwareUrl.ValueString() != state.FromServiceCloudVmwareUrl.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vmware_url", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceNamespace.IsNull() && !plan.FromServiceNamespace.IsUnknown() && !state.FromServiceNamespace.IsNull() && !state.FromServiceNamespace.IsUnknown() { + if plan.FromServiceNamespace.ValueString() != state.FromServiceNamespace.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_namespace", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceVdcGroupName.IsNull() && !plan.FromServiceVdcGroupName.IsUnknown() && !state.FromServiceVdcGroupName.IsNull() && !state.FromServiceVdcGroupName.IsUnknown() { + if plan.FromServiceVdcGroupName.ValueString() != state.FromServiceVdcGroupName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_vdc_group_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.FromServiceNamespace.IsNull() || config.FromServiceNamespace.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") + return + } + if config.InternalAddrAccess.IsNull() || config.InternalAddrAccess.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") + return + } + if config.IpSpaceName.IsNull() || config.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ServiceUid.IsNull() || config.ServiceUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 140: resources_core.FormatString(config.ServiceUid), + 141: resources_core.FormatBool(config.DnatCreate), + 142: resources_core.FormatString(config.InternalPortAccess), + 143: resources_core.FormatBool(config.SnatCreate), + 144: resources_core.FormatString(config.InternalAddrAccess), + 324: resources_core.FormatString(config.FromServiceNamespace), + 325: resources_core.FormatString(config.FromServiceCloudEdgeName), + 326: resources_core.FormatString(config.FromServiceCloudVdcName), + 327: resources_core.FormatString(config.FromServiceCloudOrgName), + 328: resources_core.FormatString(config.FromServiceCloudVmwareUrl), + 336: resources_core.FormatString(config.IpSpaceName), + 488: resources_core.FormatString(config.ResourceRealm), + 624: resources_core.FormatString(config.FromServiceCloudEdgeScope), + 626: resources_core.FormatString(config.FromServiceVdcGroupName), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 25, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcexternalipResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcexternalipModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.FromServiceNamespace.IsNull() || data.FromServiceNamespace.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") + return + } + if data.InternalAddrAccess.IsNull() || data.InternalAddrAccess.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") + return + } + if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.ServiceUid.IsNull() || data.ServiceUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 140: resources_core.FormatString(data.ServiceUid), + 141: resources_core.FormatBool(data.DnatCreate), + 142: resources_core.FormatString(data.InternalPortAccess), + 143: resources_core.FormatBool(data.SnatCreate), + 144: resources_core.FormatString(data.InternalAddrAccess), + 324: resources_core.FormatString(data.FromServiceNamespace), + 325: resources_core.FormatString(data.FromServiceCloudEdgeName), + 326: resources_core.FormatString(data.FromServiceCloudVdcName), + 327: resources_core.FormatString(data.FromServiceCloudOrgName), + 328: resources_core.FormatString(data.FromServiceCloudVmwareUrl), + 336: resources_core.FormatString(data.IpSpaceName), + 488: resources_core.FormatString(data.ResourceRealm), + 624: resources_core.FormatString(data.FromServiceCloudEdgeScope), + 626: resources_core.FormatString(data.FromServiceVdcGroupName), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 25, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, + {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, + {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, + {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, + {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, + {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, + {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, + {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, + {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, + {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, + {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcexternalipResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcexternalipModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 25, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, + {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, + {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, + {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, + {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, + {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, + {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, + {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, + {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, + {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, + {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcexternalipModel + var state VcexternalipModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ServiceUid.IsNull() != state.ServiceUid.IsNull() || plan.ServiceUid.IsUnknown() != state.ServiceUid.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ServiceUid.IsNull() && !plan.ServiceUid.IsUnknown() && !state.ServiceUid.IsNull() && !state.ServiceUid.IsUnknown() { + if plan.ServiceUid.ValueString() != state.ServiceUid.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.DnatCreate.IsNull() != state.DnatCreate.IsNull() || plan.DnatCreate.IsUnknown() != state.DnatCreate.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.DnatCreate.IsNull() && !plan.DnatCreate.IsUnknown() && !state.DnatCreate.IsNull() && !state.DnatCreate.IsUnknown() { + if plan.DnatCreate.ValueBool() != state.DnatCreate.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.SnatCreate.IsNull() != state.SnatCreate.IsNull() || plan.SnatCreate.IsUnknown() != state.SnatCreate.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.SnatCreate.IsNull() && !plan.SnatCreate.IsUnknown() && !state.SnatCreate.IsNull() && !state.SnatCreate.IsUnknown() { + if plan.SnatCreate.ValueBool() != state.SnatCreate.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.InternalPortAccess.IsNull() != state.InternalPortAccess.IsNull() || plan.InternalPortAccess.IsUnknown() != state.InternalPortAccess.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.InternalPortAccess.IsNull() && !plan.InternalPortAccess.IsUnknown() && !state.InternalPortAccess.IsNull() && !state.InternalPortAccess.IsUnknown() { + if plan.InternalPortAccess.ValueString() != state.InternalPortAccess.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.InternalAddrAccess.IsNull() != state.InternalAddrAccess.IsNull() || plan.InternalAddrAccess.IsUnknown() != state.InternalAddrAccess.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.InternalAddrAccess.IsNull() && !plan.InternalAddrAccess.IsUnknown() && !state.InternalAddrAccess.IsNull() && !state.InternalAddrAccess.IsUnknown() { + if plan.InternalAddrAccess.ValueString() != state.InternalAddrAccess.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 627: resources_core.FormatString(plan.ServiceUid), + 628: resources_core.FormatString(plan.IpSpaceName), + 629: resources_core.FormatBool(plan.DnatCreate), + 630: resources_core.FormatBool(plan.SnatCreate), + 631: resources_core.FormatString(plan.InternalPortAccess), + 632: resources_core.FormatString(plan.InternalAddrAccess), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 25, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, + {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, + {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, + {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, + {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, + {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, + {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, + {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, + {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, + {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, + {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcexternalipResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcexternalipModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcexternalipResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcexternalipResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/26_vapp_resource.go b/generated/dev/go/26_vapp_resource.go new file mode 100644 index 0000000..2044004 --- /dev/null +++ b/generated/dev/go/26_vapp_resource.go @@ -0,0 +1,496 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vapp +// Service ID: 26 + +var _ resource.Resource = &VappResource{} +var _ resource.ResourceWithModifyPlan = &VappResource{} +var _ resource.ResourceWithImportState = &VappResource{} + +type VappResource struct { + client *core.UniversalClient +} + +type VappModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + NsxtUid types.String `tfsdk:"nsxt_uid"` + VappName types.String `tfsdk:"vapp_name"` + VdcUid types.String `tfsdk:"vdc_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVappResource() resource.Resource { + return &VappResource{} +} + +func (r *VappResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vapp" +} + +func (r *VappResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "nsxt_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID Услуги `Сетевой шлюз периметра (Edge)` Не изменяется после создания."}, + "vapp_name": schema.StringAttribute{Required: true, MarkdownDescription: "Уникальное в рамках организации Не изменяется после создания."}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VappResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VappModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VappModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VappModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, plan.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != plan.NsxtUid.ValueString() { + plan.NsxtUid = types.StringValue(resolvedNsxtUid) + planChanged = true + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + if plan.NsxtUid.ValueString() != state.NsxtUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { + if plan.VappName.ValueString() != state.VappName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.NsxtUid.IsNull() || config.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if config.VappName.IsNull() || config.VappName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") + return + } + if config.VdcUid.IsNull() || config.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { + config.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + params := map[int]string{ + 190: resources_core.FormatString(config.NsxtUid), + 191: resources_core.FormatString(config.VappName), + 623: resources_core.FormatString(config.VdcUid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 26, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VappResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VappModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if data.VappName.IsNull() || data.VappName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") + return + } + if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { + data.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 190: resources_core.FormatString(data.NsxtUid), + 191: resources_core.FormatString(data.VappName), + 623: resources_core.FormatString(data.VdcUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 26, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VappResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VappModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 26, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { + newState.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VappModel + var state VappModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 26, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VappResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VappModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VappResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VappResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/28_vc_vm_v3_redeploy_action.go b/generated/dev/go/28_vc_vm_v3_redeploy_action.go similarity index 93% rename from provider/internal/resources_gen/28_vc_vm_v3_redeploy_action.go rename to generated/dev/go/28_vc_vm_v3_redeploy_action.go index d4f675c..7185491 100644 --- a/provider/internal/resources_gen/28_vc_vm_v3_redeploy_action.go +++ b/generated/dev/go/28_vc_vm_v3_redeploy_action.go @@ -66,12 +66,12 @@ func (r *VcVmV3RedeployResource) Schema(ctx context.Context, req resource.Schema "vm_ram": schema.Int64Attribute{Required: true, MarkdownDescription: "Gb"}, "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev"}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "access_ip_list": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, "image_vm": schema.StringAttribute{Required: true}, "user_login": schema.StringAttribute{Required: true, MarkdownDescription: "Учётка SSH"}, "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя"}, "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов."}, - "access_port_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "access_port_list": schema.StringAttribute{Optional: true, MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, } resp.Schema = schema.Schema{Attributes: attrs} } diff --git a/generated/dev/go/28_vc_vm_v3_resource.go b/generated/dev/go/28_vc_vm_v3_resource.go new file mode 100644 index 0000000..6673ac6 --- /dev/null +++ b/generated/dev/go/28_vc_vm_v3_resource.go @@ -0,0 +1,598 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vm_v3 +// Service ID: 28 + +var _ resource.Resource = &VcVmV3Resource{} +var _ resource.ResourceWithModifyPlan = &VcVmV3Resource{} +var _ resource.ResourceWithImportState = &VcVmV3Resource{} + +type VcVmV3Resource struct { + client *core.UniversalClient +} + +type VcVmV3Model struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AccessIpList types.String `tfsdk:"access_ip_list"` + AccessPortList types.String `tfsdk:"access_port_list"` + CloudInit types.String `tfsdk:"cloud_init"` + ImageVm types.String `tfsdk:"image_vm"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + UserLogin types.String `tfsdk:"user_login"` + UserPublicKey types.String `tfsdk:"user_public_key"` + VappUid types.String `tfsdk:"vapp_uid"` + VmCpu types.Int64 `tfsdk:"vm_cpu"` + VmDisk types.Int64 `tfsdk:"vm_disk"` + VmName types.String `tfsdk:"vm_name"` + VmRam types.Int64 `tfsdk:"vm_ram"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVmV3Resource() resource.Resource { + return &VcVmV3Resource{} +} + +func (r *VcVmV3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vm_v3" +} + +func (r *VcVmV3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_ip_list": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, + "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, + "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "ip_space_name": schema.StringAttribute{Required: true}, + "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, + "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, + "vapp_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, + "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, + "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, + "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, + "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVmV3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVmV3Model + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVmV3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVmV3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, plan.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != plan.VappUid.ValueString() { + plan.VappUid = types.StringValue(resolvedVappUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.CloudInit.IsNull() && !plan.CloudInit.IsUnknown() && !state.CloudInit.IsNull() && !state.CloudInit.IsUnknown() { + if plan.CloudInit.ValueString() != state.CloudInit.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить cloud_init", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ImageVm.IsNull() && !plan.ImageVm.IsUnknown() && !state.ImageVm.IsNull() && !state.ImageVm.IsUnknown() { + if plan.ImageVm.ValueString() != state.ImageVm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить image_vm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { + if plan.UserLogin.ValueString() != state.UserLogin.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.UserPublicKey.IsNull() && !plan.UserPublicKey.IsUnknown() && !state.UserPublicKey.IsNull() && !state.UserPublicKey.IsUnknown() { + if plan.UserPublicKey.ValueString() != state.UserPublicKey.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить user_public_key", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + if plan.VappUid.ValueString() != state.VappUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { + if plan.VmName.ValueString() != state.VmName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ImageVm.IsNull() || config.ImageVm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") + return + } + if config.IpSpaceName.IsNull() || config.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if config.UserPublicKey.IsNull() || config.UserPublicKey.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") + return + } + if config.VappUid.IsNull() || config.VappUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { + config.VappUid = types.StringValue(resolvedVappUid) + } + } + params := map[int]string{ + 407: resources_core.FormatString(config.VappUid), + 408: resources_core.FormatString(config.VmName), + 409: resources_core.FormatInt64(config.VmCpu), + 410: resources_core.FormatInt64(config.VmRam), + 411: resources_core.FormatInt64(config.VmDisk), + 412: resources_core.FormatString(config.IpSpaceName), + 413: resources_core.FormatString(config.AccessIpList), + 414: resources_core.FormatString(config.ImageVm), + 415: resources_core.FormatString(config.CloudInit), + 416: resources_core.FormatString(config.UserLogin), + 417: resources_core.FormatString(config.UserPublicKey), + 448: resources_core.FormatString(config.AccessPortList), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 28, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVmV3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVmV3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ImageVm.IsNull() || data.ImageVm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") + return + } + if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if data.UserPublicKey.IsNull() || data.UserPublicKey.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") + return + } + if data.VappUid.IsNull() || data.VappUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") + return + } + if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { + data.VappUid = types.StringValue(resolvedVappUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 407: resources_core.FormatString(data.VappUid), + 408: resources_core.FormatString(data.VmName), + 409: resources_core.FormatInt64(data.VmCpu), + 410: resources_core.FormatInt64(data.VmRam), + 411: resources_core.FormatInt64(data.VmDisk), + 412: resources_core.FormatString(data.IpSpaceName), + 413: resources_core.FormatString(data.AccessIpList), + 414: resources_core.FormatString(data.ImageVm), + 415: resources_core.FormatString(data.CloudInit), + 416: resources_core.FormatString(data.UserLogin), + 417: resources_core.FormatString(data.UserPublicKey), + 448: resources_core.FormatString(data.AccessPortList), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 28, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, + {Code: "cloudInit", Field: "CloudInit", Type: "string"}, + {Code: "imageVm", Field: "ImageVm", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, + {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { + state.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmV3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVmV3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 28, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, + {Code: "cloudInit", Field: "CloudInit", Type: "string"}, + {Code: "imageVm", Field: "ImageVm", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, + {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { + newState.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVmV3Model + var state VcVmV3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { + if plan.VmCpu.ValueInt64() != state.VmCpu.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { + if plan.VmRam.ValueInt64() != state.VmRam.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VmDisk.IsNull() != state.VmDisk.IsNull() || plan.VmDisk.IsUnknown() != state.VmDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmDisk.IsNull() && !plan.VmDisk.IsUnknown() && !state.VmDisk.IsNull() && !state.VmDisk.IsUnknown() { + if plan.VmDisk.ValueInt64() != state.VmDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { + if plan.AccessIpList.ValueString() != state.AccessIpList.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessPortList.IsNull() != state.AccessPortList.IsNull() || plan.AccessPortList.IsUnknown() != state.AccessPortList.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessPortList.IsNull() && !plan.AccessPortList.IsUnknown() && !state.AccessPortList.IsNull() && !state.AccessPortList.IsUnknown() { + if plan.AccessPortList.ValueString() != state.AccessPortList.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 493: resources_core.FormatInt64(plan.VmCpu), + 494: resources_core.FormatInt64(plan.VmRam), + 495: resources_core.FormatInt64(plan.VmDisk), + 496: resources_core.FormatString(plan.IpSpaceName), + 497: resources_core.FormatString(plan.AccessIpList), + 498: resources_core.FormatString(plan.AccessPortList), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 28, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, + {Code: "cloudInit", Field: "CloudInit", Type: "string"}, + {Code: "imageVm", Field: "ImageVm", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, + {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { + state.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmV3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVmV3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVmV3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVmV3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/29_vc_vdc_group_add_vdc_action.go b/generated/dev/go/29_vc_vdc_group_add_vdc_action.go new file mode 100644 index 0000000..5b8b517 --- /dev/null +++ b/generated/dev/go/29_vc_vdc_group_add_vdc_action.go @@ -0,0 +1,146 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vdc_group +// Action: add_vdc + +var _ resource.Resource = &VcVdcGroupAddVdcResource{} + +// VcVdcGroupAddVdcResource triggers a one-time action. +type VcVdcGroupAddVdcResource struct { + client *core.UniversalClient +} + +type VcVdcGroupAddVdcModel struct { + ID types.String `tfsdk:"id"` + VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + VdcUid types.String `tfsdk:"vdc_uid"` +} + +func NewVcVdcGroupAddVdcResource() resource.Resource { + return &VcVdcGroupAddVdcResource{} +} + +func (r *VcVdcGroupAddVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc_group_add_vdc" +} + +func (r *VcVdcGroupAddVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "vc_vdc_group_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core)"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVdcGroupAddVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan VcVdcGroupAddVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.VdcUid.IsNull() || plan.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + + instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "vdcUid": resources_core.FormatString(plan.VdcUid), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "add_vdc", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "add_vdc", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *VcVdcGroupAddVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcGroupAddVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcGroupAddVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcGroupAddVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "vdcUid": resources_core.FormatString(plan.VdcUid), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "add_vdc", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "add_vdc", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *VcVdcGroupAddVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *VcVdcGroupAddVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/29_vc_vdc_group_reconcile_action.go b/generated/dev/go/29_vc_vdc_group_reconcile_action.go similarity index 100% rename from provider/internal/resources_gen/29_vc_vdc_group_reconcile_action.go rename to generated/dev/go/29_vc_vdc_group_reconcile_action.go diff --git a/generated/dev/go/29_vc_vdc_group_remove_vdc_action.go b/generated/dev/go/29_vc_vdc_group_remove_vdc_action.go new file mode 100644 index 0000000..743d416 --- /dev/null +++ b/generated/dev/go/29_vc_vdc_group_remove_vdc_action.go @@ -0,0 +1,146 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vdc_group +// Action: remove_vdc + +var _ resource.Resource = &VcVdcGroupRemoveVdcResource{} + +// VcVdcGroupRemoveVdcResource triggers a one-time action. +type VcVdcGroupRemoveVdcResource struct { + client *core.UniversalClient +} + +type VcVdcGroupRemoveVdcModel struct { + ID types.String `tfsdk:"id"` + VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + VdcName types.String `tfsdk:"vdc_name"` +} + +func NewVcVdcGroupRemoveVdcResource() resource.Resource { + return &VcVdcGroupRemoveVdcResource{} +} + +func (r *VcVdcGroupRemoveVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc_group_remove_vdc" +} + +func (r *VcVdcGroupRemoveVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "vc_vdc_group_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "vdc_name": schema.StringAttribute{Required: true}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVdcGroupRemoveVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan VcVdcGroupRemoveVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.VdcName.IsNull() || plan.VdcName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_name is required.") + return + } + + instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "vdcName": resources_core.FormatString(plan.VdcName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "remove_vdc", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "remove_vdc", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *VcVdcGroupRemoveVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcGroupRemoveVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcGroupRemoveVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcGroupRemoveVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "vdcName": resources_core.FormatString(plan.VdcName), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "remove_vdc", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "remove_vdc", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *VcVdcGroupRemoveVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *VcVdcGroupRemoveVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/29_vc_vdc_group_resource.go b/generated/dev/go/29_vc_vdc_group_resource.go new file mode 100644 index 0000000..692769f --- /dev/null +++ b/generated/dev/go/29_vc_vdc_group_resource.go @@ -0,0 +1,400 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vdc_group +// Service ID: 29 + +var _ resource.Resource = &VcVdcGroupResource{} +var _ resource.ResourceWithModifyPlan = &VcVdcGroupResource{} +var _ resource.ResourceWithImportState = &VcVdcGroupResource{} + +type VcVdcGroupResource struct { + client *core.UniversalClient +} + +type VcVdcGroupModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + VdcUid types.String `tfsdk:"vdc_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVdcGroupResource() resource.Resource { + return &VcVdcGroupResource{} +} + +func (r *VcVdcGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc_group" +} + +func (r *VcVdcGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core) Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVdcGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVdcGroupModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVdcGroupModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVdcGroupModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.VdcUid.IsNull() || config.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + params := map[int]string{ + 618: resources_core.FormatString(config.VdcUid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 29, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVdcGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVdcGroupModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 618: resources_core.FormatString(data.VdcUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 29, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcGroupModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 29, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcGroupModel + var state VcVdcGroupModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 29, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVdcGroupModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVdcGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVdcGroupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/2_template_resource.go b/generated/dev/go/2_template_resource.go new file mode 100644 index 0000000..01b8a84 --- /dev/null +++ b/generated/dev/go/2_template_resource.go @@ -0,0 +1,435 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: template +// Service ID: 2 + +var _ resource.Resource = &TemplateResource{} +var _ resource.ResourceWithModifyPlan = &TemplateResource{} +var _ resource.ResourceWithImportState = &TemplateResource{} + +type TemplateResource struct { + client *core.UniversalClient +} + +type TemplateModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewTemplateResource() resource.Resource { + return &TemplateResource{} +} + +func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_template" +} + +func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100)}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200)}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *TemplateResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *TemplateModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *TemplateModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan TemplateModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 202: resources_core.FormatInt64(config.ResourceCPU), + 203: resources_core.FormatInt64(config.ResourceMemory), + 204: resources_core.FormatInt64(config.ResourceDisk), + 205: resources_core.FormatInt64(config.ResourceInstances), + 206: resources_core.FormatString(config.ResourceRealm), + 207: resources_core.FormatString(config.Domain), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 2, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *TemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data TemplateModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 202: resources_core.FormatInt64(data.ResourceCPU), + 203: resources_core.FormatInt64(data.ResourceMemory), + 204: resources_core.FormatInt64(data.ResourceDisk), + 205: resources_core.FormatInt64(data.ResourceInstances), + 206: resources_core.FormatString(data.ResourceRealm), + 207: resources_core.FormatString(data.Domain), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 2, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state TemplateModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 2, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan TemplateModel + var state TemplateModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 208: resources_core.FormatInt64(plan.ResourceCPU), + 209: resources_core.FormatInt64(plan.ResourceMemory), + 210: resources_core.FormatInt64(plan.ResourceDisk), + 211: resources_core.FormatInt64(plan.ResourceInstances), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 2, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state TemplateModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *TemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/50_nextcloud_resource.go b/generated/dev/go/50_nextcloud_resource.go new file mode 100644 index 0000000..635d071 --- /dev/null +++ b/generated/dev/go/50_nextcloud_resource.go @@ -0,0 +1,595 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nextcloud +// Service ID: 50 + +var _ resource.Resource = &NextcloudResource{} +var _ resource.ResourceWithModifyPlan = &NextcloudResource{} +var _ resource.ResourceWithImportState = &NextcloudResource{} + +type NextcloudResource struct { + client *core.UniversalClient +} + +type NextcloudModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AppVersion types.String `tfsdk:"app_version"` + DocumentServer types.String `tfsdk:"document_server"` + Domain types.String `tfsdk:"domain"` + PsqlUid types.String `tfsdk:"psql_uid"` + RedisUid types.String `tfsdk:"redis_uid"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNextcloudResource() resource.Resource { + return &NextcloudResource{} +} + +func (r *NextcloudResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nextcloud" +} + +func (r *NextcloudResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("32.0.1")}, + "document_server": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("collabora"), MarkdownDescription: "Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ Не изменяется после создания."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=90 (mapped to UUID in core) Не изменяется после создания."}, + "redis_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=91 (mapped to UUID in core) Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)
Диск не может быть меньше 2G (!)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов
Не может пока быть больше одного, так как нужны SC под RWMany"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NextcloudResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NextcloudModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NextcloudModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NextcloudModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, plan.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != plan.PsqlUid.ValueString() { + plan.PsqlUid = types.StringValue(resolvedPsqlUid) + planChanged = true + } + } + if !plan.RedisUid.IsNull() && !plan.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, plan.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != plan.RedisUid.ValueString() { + plan.RedisUid = types.StringValue(resolvedRedisUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.DocumentServer.IsNull() && !plan.DocumentServer.IsUnknown() && !state.DocumentServer.IsNull() && !state.DocumentServer.IsUnknown() { + if plan.DocumentServer.ValueString() != state.DocumentServer.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить document_server", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + if plan.PsqlUid.ValueString() != state.PsqlUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RedisUid.IsNull() && !plan.RedisUid.IsUnknown() && !state.RedisUid.IsNull() && !state.RedisUid.IsUnknown() { + if plan.RedisUid.ValueString() != state.RedisUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить redis_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.PsqlUid.IsNull() || config.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + if config.RedisUid.IsNull() || config.RedisUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "redis_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { + config.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !config.RedisUid.IsNull() && !config.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, config.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != config.RedisUid.ValueString() { + config.RedisUid = types.StringValue(resolvedRedisUid) + } + } + params := map[int]string{ + 21: resources_core.FormatString(config.Domain), + 99: resources_core.FormatInt64(config.ResourceCPU), + 100: resources_core.FormatInt64(config.ResourceMemory), + 101: resources_core.FormatInt64(config.ResourceDisk), + 192: resources_core.FormatString(config.DocumentServer), + 470: resources_core.FormatInt64(config.ResourceInstances), + 564: resources_core.FormatString(config.PsqlUid), + 569: resources_core.FormatString(config.AppVersion), + 570: resources_core.FormatString(config.RedisUid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 50, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NextcloudResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NextcloudModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + if data.RedisUid.IsNull() || data.RedisUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "redis_uid is required.") + return + } + if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { + data.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !data.RedisUid.IsNull() && !data.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, data.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedRedisUid != "" && resolvedRedisUid != data.RedisUid.ValueString() { + data.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 21: resources_core.FormatString(data.Domain), + 99: resources_core.FormatInt64(data.ResourceCPU), + 100: resources_core.FormatInt64(data.ResourceMemory), + 101: resources_core.FormatInt64(data.ResourceDisk), + 192: resources_core.FormatString(data.DocumentServer), + 470: resources_core.FormatInt64(data.ResourceInstances), + 564: resources_core.FormatString(data.PsqlUid), + 569: resources_core.FormatString(data.AppVersion), + 570: resources_core.FormatString(data.RedisUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 50, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "documentServer", Field: "DocumentServer", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "redisUid", Field: "RedisUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !state.RedisUid.IsNull() && !state.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, state.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != state.RedisUid.ValueString() { + state.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NextcloudResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NextcloudModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 50, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "documentServer", Field: "DocumentServer", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "redisUid", Field: "RedisUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { + newState.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !newState.RedisUid.IsNull() && !newState.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, newState.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != newState.RedisUid.ValueString() { + newState.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NextcloudModel + var state NextcloudModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 178: resources_core.FormatInt64(plan.ResourceCPU), + 179: resources_core.FormatInt64(plan.ResourceMemory), + 180: resources_core.FormatInt64(plan.ResourceDisk), + 547: resources_core.FormatInt64(plan.ResourceInstances), + 571: resources_core.FormatString(plan.AppVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 50, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "documentServer", Field: "DocumentServer", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "redisUid", Field: "RedisUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !state.RedisUid.IsNull() && !state.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, state.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != state.RedisUid.ValueString() { + state.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NextcloudResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NextcloudModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NextcloudResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NextcloudResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/50_nextcloud_restart_action.go b/generated/dev/go/50_nextcloud_restart_action.go similarity index 100% rename from provider/internal/resources_gen/50_nextcloud_restart_action.go rename to generated/dev/go/50_nextcloud_restart_action.go diff --git a/generated/dev/go/81_superset_resource.go b/generated/dev/go/81_superset_resource.go new file mode 100644 index 0000000..e8c019c --- /dev/null +++ b/generated/dev/go/81_superset_resource.go @@ -0,0 +1,428 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: superset +// Service ID: 81 + +var _ resource.Resource = &SupersetResource{} +var _ resource.ResourceWithModifyPlan = &SupersetResource{} +var _ resource.ResourceWithImportState = &SupersetResource{} + +type SupersetResource struct { + client *core.UniversalClient +} + +type SupersetModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + Emails types.String `tfsdk:"emails"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewSupersetResource() resource.Resource { + return &SupersetResource{} +} + +func (r *SupersetResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_superset" +} + +func (r *SupersetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *SupersetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *SupersetModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *SupersetModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan SupersetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { + if plan.Emails.ValueString() != state.Emails.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 14: resources_core.FormatString(config.Domain), + 20: resources_core.FormatString(config.Emails), + 96: resources_core.FormatInt64(config.ResourceCPU), + 97: resources_core.FormatInt64(config.ResourceMemory), + 98: resources_core.FormatInt64(config.ResourceDisk), + 109: resources_core.FormatString(config.ResourceRealm), + 227: resources_core.FormatInt64(config.ResourceInstances), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 81, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *SupersetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SupersetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 14: resources_core.FormatString(data.Domain), + 20: resources_core.FormatString(data.Emails), + 96: resources_core.FormatInt64(data.ResourceCPU), + 97: resources_core.FormatInt64(data.ResourceMemory), + 98: resources_core.FormatInt64(data.ResourceDisk), + 109: resources_core.FormatString(data.ResourceRealm), + 227: resources_core.FormatInt64(data.ResourceInstances), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 81, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *SupersetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state SupersetModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 81, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan SupersetModel + var state SupersetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 81, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *SupersetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state SupersetModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *SupersetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *SupersetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/82_harbor_resource.go b/generated/dev/go/82_harbor_resource.go new file mode 100644 index 0000000..43a76f0 --- /dev/null +++ b/generated/dev/go/82_harbor_resource.go @@ -0,0 +1,457 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: harbor +// Service ID: 82 + +var _ resource.Resource = &HarborResource{} +var _ resource.ResourceWithModifyPlan = &HarborResource{} +var _ resource.ResourceWithImportState = &HarborResource{} + +type HarborResource struct { + client *core.UniversalClient +} + +type HarborModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + Emails types.String `tfsdk:"emails"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewHarborResource() resource.Resource { + return &HarborResource{} +} + +func (r *HarborResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_harbor" +} + +func (r *HarborResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID экземпляра услуги S3 Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *HarborResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *HarborModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *HarborModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan HarborModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { + if plan.Emails.ValueString() != state.Emails.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 16: resources_core.FormatString(config.Domain), + 17: resources_core.FormatString(config.Emails), + 110: resources_core.FormatString(config.ResourceRealm), + 228: resources_core.FormatInt64(config.ResourceCPU), + 229: resources_core.FormatInt64(config.ResourceMemory), + 230: resources_core.FormatInt64(config.ResourceInstances), + 231: resources_core.FormatString(config.S3Uid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 82, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *HarborResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data HarborModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 16: resources_core.FormatString(data.Domain), + 17: resources_core.FormatString(data.Emails), + 110: resources_core.FormatString(data.ResourceRealm), + 228: resources_core.FormatInt64(data.ResourceCPU), + 229: resources_core.FormatInt64(data.ResourceMemory), + 230: resources_core.FormatInt64(data.ResourceInstances), + 231: resources_core.FormatString(data.S3Uid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 82, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HarborResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state HarborModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 82, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan HarborModel + var state HarborModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 82, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HarborResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state HarborModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *HarborResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *HarborResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/88_k8s_ziti_controller_resource.go b/generated/dev/go/88_k8s_ziti_controller_resource.go new file mode 100644 index 0000000..e283c47 --- /dev/null +++ b/generated/dev/go/88_k8s_ziti_controller_resource.go @@ -0,0 +1,415 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_ziti_controller +// Service ID: 88 + +var _ resource.Resource = &K8sZitiControllerResource{} +var _ resource.ResourceWithModifyPlan = &K8sZitiControllerResource{} +var _ resource.ResourceWithImportState = &K8sZitiControllerResource{} + +type K8sZitiControllerResource struct { + client *core.UniversalClient +} + +type K8sZitiControllerModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewK8sZitiControllerResource() resource.Resource { + return &K8sZitiControllerResource{} +} + +func (r *K8sZitiControllerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_ziti_controller" +} + +func (r *K8sZitiControllerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores) Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sZitiControllerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *K8sZitiControllerModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *K8sZitiControllerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan K8sZitiControllerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 294: resources_core.FormatInt64(config.ResourceCPU), + 295: resources_core.FormatInt64(config.ResourceMemory), + 296: resources_core.FormatInt64(config.ResourceDisk), + 297: resources_core.FormatInt64(config.ResourceInstances), + 298: resources_core.FormatString(config.ResourceRealm), + 299: resources_core.FormatString(config.Domain), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 88, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *K8sZitiControllerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data K8sZitiControllerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 294: resources_core.FormatInt64(data.ResourceCPU), + 295: resources_core.FormatInt64(data.ResourceMemory), + 296: resources_core.FormatInt64(data.ResourceDisk), + 297: resources_core.FormatInt64(data.ResourceInstances), + 298: resources_core.FormatString(data.ResourceRealm), + 299: resources_core.FormatString(data.Domain), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 88, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sZitiControllerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sZitiControllerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 88, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sZitiControllerModel + var state K8sZitiControllerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 88, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sZitiControllerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sZitiControllerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *K8sZitiControllerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *K8sZitiControllerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/89_flask_resource.go b/generated/dev/go/89_flask_resource.go new file mode 100644 index 0000000..04646aa --- /dev/null +++ b/generated/dev/go/89_flask_resource.go @@ -0,0 +1,465 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: flask +// Service ID: 89 + +var _ resource.Resource = &FlaskResource{} +var _ resource.ResourceWithModifyPlan = &FlaskResource{} +var _ resource.ResourceWithImportState = &FlaskResource{} + +type FlaskResource struct { + client *core.UniversalClient +} + +type FlaskModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + GitPath types.String `tfsdk:"git_path"` + HealthPath types.String `tfsdk:"health_path"` + JsonEnv types.String `tfsdk:"json_env"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewFlaskResource() resource.Resource { + return &FlaskResource{} +} + +func (r *FlaskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_flask" +} + +func (r *FlaskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан `FQDN`, то происходят проверки DNS на валидность А-записи. А-запись **обязана смотреть на балансировщик** ресурсной платформы.
Если указано имя `в рамках одного октета`, будет добавлено **в публичную днс-зону** Не изменяется после создания."}, + "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/Foxyhhd/Baldurs-Gate-test.git"), MarkdownDescription: "Пример: `https://github.com/myuser/myproject.git`"}, + "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `/api/v1/health` Не изменяется после создания."}, + "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Структура, которая будет передана как переменные окружения"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Пример: `1000`"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `3`"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Пример: `2048`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *FlaskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *FlaskModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *FlaskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan FlaskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.HealthPath.IsNull() && !plan.HealthPath.IsUnknown() && !state.HealthPath.IsNull() && !state.HealthPath.IsUnknown() { + if plan.HealthPath.ValueString() != state.HealthPath.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить health_path", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 248: resources_core.FormatString(config.Domain), + 249: resources_core.FormatString(config.ResourceRealm), + 250: resources_core.FormatInt64(config.ResourceCPU), + 251: resources_core.FormatInt64(config.ResourceMemory), + 252: resources_core.FormatInt64(config.ResourceInstances), + 253: resources_core.FormatString(config.GitPath), + 254: resources_core.FormatString(config.JsonEnv), + 255: resources_core.FormatString(config.HealthPath), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 89, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *FlaskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data FlaskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 248: resources_core.FormatString(data.Domain), + 249: resources_core.FormatString(data.ResourceRealm), + 250: resources_core.FormatInt64(data.ResourceCPU), + 251: resources_core.FormatInt64(data.ResourceMemory), + 252: resources_core.FormatInt64(data.ResourceInstances), + 253: resources_core.FormatString(data.GitPath), + 254: resources_core.FormatString(data.JsonEnv), + 255: resources_core.FormatString(data.HealthPath), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 89, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *FlaskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state FlaskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 89, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan FlaskModel + var state FlaskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.GitPath.IsNull() != state.GitPath.IsNull() || plan.GitPath.IsUnknown() != state.GitPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.GitPath.IsNull() && !plan.GitPath.IsUnknown() && !state.GitPath.IsNull() && !state.GitPath.IsUnknown() { + if plan.GitPath.ValueString() != state.GitPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 451: resources_core.FormatInt64(plan.ResourceCPU), + 452: resources_core.FormatInt64(plan.ResourceMemory), + 453: resources_core.FormatInt64(plan.ResourceInstances), + 454: resources_core.FormatString(plan.GitPath), + 455: resources_core.FormatString(plan.JsonEnv), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 89, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *FlaskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state FlaskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *FlaskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *FlaskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/90_postgres_database_resource.go b/generated/dev/go/90_postgres_database_resource.go new file mode 100644 index 0000000..80ef7e6 --- /dev/null +++ b/generated/dev/go/90_postgres_database_resource.go @@ -0,0 +1,362 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: postgres +// Subresource: database + +var _ resource.Resource = &PostgresDatabaseResource{} + +// PostgresDatabaseResource manages a subresource via operations. +type PostgresDatabaseResource struct { + client *core.UniversalClient +} + +type PostgresDatabaseModel struct { + ID types.String `tfsdk:"id"` + PostgresID types.String `tfsdk:"postgres_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + DbName types.String `tfsdk:"db_name"` + DbOwner types.String `tfsdk:"db_owner"` +} + +func NewPostgresDatabaseResource() resource.Resource { + return &PostgresDatabaseResource{} +} + +func (r *PostgresDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_postgres_database" +} + +func (r *PostgresDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "postgres_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "db_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "db_owner": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PostgresDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PostgresDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.DbName.IsNull() || plan.DbName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") + return + } + if plan.DbOwner.IsNull() || plan.DbOwner.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "db_owner is required.") + return + } + + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "dbName": resources_core.FormatString(plan.DbName), + "dbOwner": resources_core.FormatString(plan.DbOwner), + } + identityCodes := []string{ + "dbName", + "dbOwner", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "dbName": resources_core.FormatString(plan.DbName), + "dbOwner": resources_core.FormatString(plan.DbOwner), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PostgresDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PostgresDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PostgresDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PostgresDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state PostgresDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "dbName": resources_core.FormatString(plan.DbName), + "dbOwner": resources_core.FormatString(plan.DbOwner), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PostgresDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PostgresDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_database" == "" { + return + } + + instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "dbName": resources_core.FormatString(state.DbName), + "dbOwner": resources_core.FormatString(state.DbOwner), + } + identityCodes := []string{ + "dbName", + "dbOwner", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "dbName": resources_core.FormatString(state.DbName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *PostgresDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/90_postgres_recovery_action.go b/generated/dev/go/90_postgres_recovery_action.go similarity index 57% rename from provider/internal/resources_gen/90_postgres_recovery_action.go rename to generated/dev/go/90_postgres_recovery_action.go index 87c21a8..afef981 100644 --- a/provider/internal/resources_gen/90_postgres_recovery_action.go +++ b/generated/dev/go/90_postgres_recovery_action.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -25,19 +26,17 @@ type PostgresRecoveryResource struct { } type PostgresRecoveryModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - RunID types.String `tfsdk:"run_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` - ClusterConfiguration types.String `tfsdk:"cluster_configuration"` - AccessConfiguration types.String `tfsdk:"access_configuration"` - PostgresConfiguration types.String `tfsdk:"postgres_configuration"` - PostgresConf types.String `tfsdk:"postgres_conf"` - BackupConfiguration types.String `tfsdk:"backup_configuration"` - AutoscaleConfiguration types.String `tfsdk:"autoscale_configuration"` + ID types.String `tfsdk:"id"` + PostgresID types.String `tfsdk:"postgres_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceRealm types.String `tfsdk:"resource_realm"` } func NewPostgresRecoveryResource() resource.Resource { @@ -56,13 +55,11 @@ func (r *PostgresRecoveryResource) Schema(ctx context.Context, req resource.Sche "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true}, - "cluster_configuration": schema.StringAttribute{Required: true}, - "access_configuration": schema.StringAttribute{Required: true}, - "postgres_configuration": schema.StringAttribute{Required: true}, - "postgres_conf": schema.StringAttribute{Optional: true}, - "backup_configuration": schema.StringAttribute{Required: true}, - "autoscale_configuration": schema.StringAttribute{Required: true}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)
"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili)
"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, } resp.Schema = schema.Schema{Attributes: attrs} } @@ -73,28 +70,8 @@ func (r *PostgresRecoveryResource) Create(ctx context.Context, req resource.Crea if resp.Diagnostics.HasError() { return } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") - return - } - if plan.ClusterConfiguration.IsNull() || plan.ClusterConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "cluster_configuration is required.") - return - } - if plan.AccessConfiguration.IsNull() || plan.AccessConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "access_configuration is required.") - return - } - if plan.PostgresConfiguration.IsNull() || plan.PostgresConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "postgres_configuration is required.") - return - } - if plan.BackupConfiguration.IsNull() || plan.BackupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "backup_configuration is required.") - return - } - if plan.AutoscaleConfiguration.IsNull() || plan.AutoscaleConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "autoscale_configuration is required.") + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return } @@ -106,13 +83,11 @@ func (r *PostgresRecoveryResource) Create(ctx context.Context, req resource.Crea } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - "clusterConfiguration": resources_core.FormatString(plan.ClusterConfiguration), - "accessConfiguration": resources_core.FormatString(plan.AccessConfiguration), - "postgresConfiguration": resources_core.FormatString(plan.PostgresConfiguration), - "postgresConf": resources_core.FormatString(plan.PostgresConf), - "backupConfiguration": resources_core.FormatString(plan.BackupConfiguration), - "autoscaleConfiguration": resources_core.FormatString(plan.AutoscaleConfiguration), + "resourceInstances": resources_core.FormatInt64(plan.ResourceInstances), + "resourceMemory": resources_core.FormatInt64(plan.ResourceMemory), + "resourceCPU": resources_core.FormatInt64(plan.ResourceCPU), + "resourceDisk": resources_core.FormatInt64(plan.ResourceDisk), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } operationTimeout := "" @@ -155,13 +130,11 @@ func (r *PostgresRecoveryResource) Update(ctx context.Context, req resource.Upda } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - "clusterConfiguration": resources_core.FormatString(plan.ClusterConfiguration), - "accessConfiguration": resources_core.FormatString(plan.AccessConfiguration), - "postgresConfiguration": resources_core.FormatString(plan.PostgresConfiguration), - "postgresConf": resources_core.FormatString(plan.PostgresConf), - "backupConfiguration": resources_core.FormatString(plan.BackupConfiguration), - "autoscaleConfiguration": resources_core.FormatString(plan.AutoscaleConfiguration), + "resourceInstances": resources_core.FormatInt64(plan.ResourceInstances), + "resourceMemory": resources_core.FormatInt64(plan.ResourceMemory), + "resourceCPU": resources_core.FormatInt64(plan.ResourceCPU), + "resourceDisk": resources_core.FormatInt64(plan.ResourceDisk), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "recovery", params); err != nil { diff --git a/generated/dev/go/90_postgres_resource.go b/generated/dev/go/90_postgres_resource.go new file mode 100644 index 0000000..5b2413e --- /dev/null +++ b/generated/dev/go/90_postgres_resource.go @@ -0,0 +1,698 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: postgres +// Service ID: 90 + +var _ resource.Resource = &PostgresResource{} +var _ resource.ResourceWithModifyPlan = &PostgresResource{} +var _ resource.ResourceWithImportState = &PostgresResource{} + +type PostgresResource struct { + client *core.UniversalClient +} + +type PostgresModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AllowNoSSL types.Bool `tfsdk:"allow_no_s_s_l"` + AppVersion types.String `tfsdk:"app_version"` + AutoScale types.Bool `tfsdk:"auto_scale"` + AutoScalePercentage types.Int64 `tfsdk:"auto_scale_percentage"` + AutoScaleQuotaGb types.String `tfsdk:"auto_scale_quota_gb"` + AutoScaleTechWindow types.Int64 `tfsdk:"auto_scale_tech_window"` + EnablePgPoolerMaster types.Bool `tfsdk:"enable_pg_pooler_master"` + EnablePgPoolerSlave types.Bool `tfsdk:"enable_pg_pooler_slave"` + ExtBACKUPNUMTORETAIN types.Int64 `tfsdk:"ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n"` + ExtBACKUPSCHEDULE types.String `tfsdk:"ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + IpSpaceNameSlave types.String `tfsdk:"ip_space_name_slave"` + JsonParameters types.String `tfsdk:"json_parameters"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + NeedExternalAddressSlave types.Bool `tfsdk:"need_external_address_slave"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.String `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewPostgresResource() resource.Resource { + return &PostgresResource{} +} + +func (r *PostgresResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_postgres" +} + +func (r *PostgresResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "allow_no_s_s_l": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр"}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("17"), MarkdownDescription: "Версия PostgreSQL"}, + "auto_scale": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги Не изменяется после создания."}, + "auto_scale_percentage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема Не изменяется после создания."}, + "auto_scale_quota_gb": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Квота для autoScale Не изменяется после создания."}, + "auto_scale_tech_window": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) Не изменяется после создания."}, + "enable_pg_pooler_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на запись). Будет создан новый под"}, + "enable_pg_pooler_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на чтение). Будет создан новый под"}, + "ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7), MarkdownDescription: "Не обязательный. Количество сохраняемых бекапов"}, + "ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *"), MarkdownDescription: "Не обязательный. Время запуска бекапа"}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`"}, + "ip_space_name_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave"}, + "json_parameters": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{ \"log_connections\": \"off\", \"log_disconnections\": \"off\" }"), MarkdownDescription: "Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний (белый) IP для доступа к master"}, + "need_external_address_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores)"}, + "resource_disk": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("10"), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PostgresResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *PostgresModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *PostgresModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan PostgresModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.AutoScale.IsNull() && !plan.AutoScale.IsUnknown() && !state.AutoScale.IsNull() && !state.AutoScale.IsUnknown() { + if plan.AutoScale.ValueBool() != state.AutoScale.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.AutoScalePercentage.IsNull() && !plan.AutoScalePercentage.IsUnknown() && !state.AutoScalePercentage.IsNull() && !state.AutoScalePercentage.IsUnknown() { + if plan.AutoScalePercentage.ValueInt64() != state.AutoScalePercentage.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale_percentage", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.AutoScaleQuotaGb.IsNull() && !plan.AutoScaleQuotaGb.IsUnknown() && !state.AutoScaleQuotaGb.IsNull() && !state.AutoScaleQuotaGb.IsUnknown() { + if plan.AutoScaleQuotaGb.ValueString() != state.AutoScaleQuotaGb.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale_quota_gb", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.AutoScaleTechWindow.IsNull() && !plan.AutoScaleTechWindow.IsUnknown() && !state.AutoScaleTechWindow.IsNull() && !state.AutoScaleTechWindow.IsUnknown() { + if plan.AutoScaleTechWindow.ValueInt64() != state.AutoScaleTechWindow.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale_tech_window", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 23: resources_core.FormatString(config.S3Uid), + 80: resources_core.FormatInt64(config.ResourceInstances), + 81: resources_core.FormatInt64(config.ResourceMemory), + 82: resources_core.FormatInt64(config.ResourceCPU), + 83: resources_core.FormatString(config.ResourceDisk), + 102: resources_core.FormatString(config.ResourceRealm), + 145: resources_core.FormatBool(config.NeedExternalAddressMaster), + 146: resources_core.FormatBool(config.NeedExternalAddressSlave), + 265: resources_core.FormatString(config.ExtBACKUPSCHEDULE), + 266: resources_core.FormatInt64(config.ExtBACKUPNUMTORETAIN), + 310: resources_core.FormatString(config.AppVersion), + 311: resources_core.FormatString(config.JsonParameters), + 312: resources_core.FormatBool(config.EnablePgPoolerMaster), + 313: resources_core.FormatBool(config.EnablePgPoolerSlave), + 314: resources_core.FormatBool(config.AllowNoSSL), + 329: resources_core.FormatBool(config.AutoScale), + 330: resources_core.FormatInt64(config.AutoScalePercentage), + 331: resources_core.FormatInt64(config.AutoScaleTechWindow), + 337: resources_core.FormatString(config.IpSpaceNameMaster), + 338: resources_core.FormatString(config.IpSpaceNameSlave), + 339: resources_core.FormatString(config.AutoScaleQuotaGb), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 90, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *PostgresResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data PostgresModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 23: resources_core.FormatString(data.S3Uid), + 80: resources_core.FormatInt64(data.ResourceInstances), + 81: resources_core.FormatInt64(data.ResourceMemory), + 82: resources_core.FormatInt64(data.ResourceCPU), + 83: resources_core.FormatString(data.ResourceDisk), + 102: resources_core.FormatString(data.ResourceRealm), + 145: resources_core.FormatBool(data.NeedExternalAddressMaster), + 146: resources_core.FormatBool(data.NeedExternalAddressSlave), + 265: resources_core.FormatString(data.ExtBACKUPSCHEDULE), + 266: resources_core.FormatInt64(data.ExtBACKUPNUMTORETAIN), + 310: resources_core.FormatString(data.AppVersion), + 311: resources_core.FormatString(data.JsonParameters), + 312: resources_core.FormatBool(data.EnablePgPoolerMaster), + 313: resources_core.FormatBool(data.EnablePgPoolerSlave), + 314: resources_core.FormatBool(data.AllowNoSSL), + 329: resources_core.FormatBool(data.AutoScale), + 330: resources_core.FormatInt64(data.AutoScalePercentage), + 331: resources_core.FormatInt64(data.AutoScaleTechWindow), + 337: resources_core.FormatString(data.IpSpaceNameMaster), + 338: resources_core.FormatString(data.IpSpaceNameSlave), + 339: resources_core.FormatString(data.AutoScaleQuotaGb), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 90, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "allowNoSSL", Field: "AllowNoSSL", Type: "bool"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "string"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "enablePgPoolerMaster", Field: "EnablePgPoolerMaster", Type: "bool"}, + {Code: "enablePgPoolerSlave", Field: "EnablePgPoolerSlave", Type: "bool"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "jsonParameters", Field: "JsonParameters", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "string"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PostgresResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PostgresModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 90, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "allowNoSSL", Field: "AllowNoSSL", Type: "bool"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "string"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "enablePgPoolerMaster", Field: "EnablePgPoolerMaster", Type: "bool"}, + {Code: "enablePgPoolerSlave", Field: "EnablePgPoolerSlave", Type: "bool"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "jsonParameters", Field: "JsonParameters", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "string"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PostgresModel + var state PostgresModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueString() != state.ResourceDisk.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressSlave.IsNull() != state.NeedExternalAddressSlave.IsNull() || plan.NeedExternalAddressSlave.IsUnknown() != state.NeedExternalAddressSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressSlave.IsNull() && !plan.NeedExternalAddressSlave.IsUnknown() && !state.NeedExternalAddressSlave.IsNull() && !state.NeedExternalAddressSlave.IsUnknown() { + if plan.NeedExternalAddressSlave.ValueBool() != state.NeedExternalAddressSlave.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPSCHEDULE.IsNull() != state.ExtBACKUPSCHEDULE.IsNull() || plan.ExtBACKUPSCHEDULE.IsUnknown() != state.ExtBACKUPSCHEDULE.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPSCHEDULE.IsNull() && !plan.ExtBACKUPSCHEDULE.IsUnknown() && !state.ExtBACKUPSCHEDULE.IsNull() && !state.ExtBACKUPSCHEDULE.IsUnknown() { + if plan.ExtBACKUPSCHEDULE.ValueString() != state.ExtBACKUPSCHEDULE.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPNUMTORETAIN.IsNull() != state.ExtBACKUPNUMTORETAIN.IsNull() || plan.ExtBACKUPNUMTORETAIN.IsUnknown() != state.ExtBACKUPNUMTORETAIN.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPNUMTORETAIN.IsNull() && !plan.ExtBACKUPNUMTORETAIN.IsUnknown() && !state.ExtBACKUPNUMTORETAIN.IsNull() && !state.ExtBACKUPNUMTORETAIN.IsUnknown() { + if plan.ExtBACKUPNUMTORETAIN.ValueInt64() != state.ExtBACKUPNUMTORETAIN.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonParameters.IsNull() != state.JsonParameters.IsNull() || plan.JsonParameters.IsUnknown() != state.JsonParameters.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonParameters.IsNull() && !plan.JsonParameters.IsUnknown() && !state.JsonParameters.IsNull() && !state.JsonParameters.IsUnknown() { + if plan.JsonParameters.ValueString() != state.JsonParameters.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.EnablePgPoolerMaster.IsNull() != state.EnablePgPoolerMaster.IsNull() || plan.EnablePgPoolerMaster.IsUnknown() != state.EnablePgPoolerMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.EnablePgPoolerMaster.IsNull() && !plan.EnablePgPoolerMaster.IsUnknown() && !state.EnablePgPoolerMaster.IsNull() && !state.EnablePgPoolerMaster.IsUnknown() { + if plan.EnablePgPoolerMaster.ValueBool() != state.EnablePgPoolerMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.EnablePgPoolerSlave.IsNull() != state.EnablePgPoolerSlave.IsNull() || plan.EnablePgPoolerSlave.IsUnknown() != state.EnablePgPoolerSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.EnablePgPoolerSlave.IsNull() && !plan.EnablePgPoolerSlave.IsUnknown() && !state.EnablePgPoolerSlave.IsNull() && !state.EnablePgPoolerSlave.IsUnknown() { + if plan.EnablePgPoolerSlave.ValueBool() != state.EnablePgPoolerSlave.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AllowNoSSL.IsNull() != state.AllowNoSSL.IsNull() || plan.AllowNoSSL.IsUnknown() != state.AllowNoSSL.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AllowNoSSL.IsNull() && !plan.AllowNoSSL.IsUnknown() && !state.AllowNoSSL.IsNull() && !state.AllowNoSSL.IsUnknown() { + if plan.AllowNoSSL.ValueBool() != state.AllowNoSSL.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameSlave.IsNull() != state.IpSpaceNameSlave.IsNull() || plan.IpSpaceNameSlave.IsUnknown() != state.IpSpaceNameSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameSlave.IsNull() && !plan.IpSpaceNameSlave.IsUnknown() && !state.IpSpaceNameSlave.IsNull() && !state.IpSpaceNameSlave.IsUnknown() { + if plan.IpSpaceNameSlave.ValueString() != state.IpSpaceNameSlave.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 91: resources_core.FormatInt64(plan.ResourceInstances), + 92: resources_core.FormatInt64(plan.ResourceMemory), + 93: resources_core.FormatInt64(plan.ResourceCPU), + 94: resources_core.FormatString(plan.ResourceDisk), + 147: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 148: resources_core.FormatBool(plan.NeedExternalAddressSlave), + 267: resources_core.FormatString(plan.ExtBACKUPSCHEDULE), + 268: resources_core.FormatInt64(plan.ExtBACKUPNUMTORETAIN), + 315: resources_core.FormatString(plan.AppVersion), + 316: resources_core.FormatString(plan.JsonParameters), + 317: resources_core.FormatBool(plan.EnablePgPoolerMaster), + 318: resources_core.FormatBool(plan.EnablePgPoolerSlave), + 319: resources_core.FormatBool(plan.AllowNoSSL), + 541: resources_core.FormatString(plan.IpSpaceNameMaster), + 542: resources_core.FormatString(plan.IpSpaceNameSlave), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 90, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "allowNoSSL", Field: "AllowNoSSL", Type: "bool"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "string"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "enablePgPoolerMaster", Field: "EnablePgPoolerMaster", Type: "bool"}, + {Code: "enablePgPoolerSlave", Field: "EnablePgPoolerSlave", Type: "bool"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "jsonParameters", Field: "JsonParameters", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "string"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PostgresResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PostgresModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *PostgresResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *PostgresResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/90_postgres_restart_action.go b/generated/dev/go/90_postgres_restart_action.go similarity index 100% rename from provider/internal/resources_gen/90_postgres_restart_action.go rename to generated/dev/go/90_postgres_restart_action.go diff --git a/generated/dev/go/90_postgres_user_resource.go b/generated/dev/go/90_postgres_user_resource.go new file mode 100644 index 0000000..05ed7f4 --- /dev/null +++ b/generated/dev/go/90_postgres_user_resource.go @@ -0,0 +1,362 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: postgres +// Subresource: user + +var _ resource.Resource = &PostgresUserResource{} + +// PostgresUserResource manages a subresource via operations. +type PostgresUserResource struct { + client *core.UniversalClient +} + +type PostgresUserModel struct { + ID types.String `tfsdk:"id"` + PostgresID types.String `tfsdk:"postgres_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Role types.String `tfsdk:"role"` + Username types.String `tfsdk:"username"` +} + +func NewPostgresUserResource() resource.Resource { + return &PostgresUserResource{} +} + +func (r *PostgresUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_postgres_user" +} + +func (r *PostgresUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "postgres_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "role": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать роль из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PostgresUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PostgresUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.Role.IsNull() || plan.Role.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "role is required.") + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "role": resources_core.FormatString(plan.Role), + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "role", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "role": resources_core.FormatString(plan.Role), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PostgresUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PostgresUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PostgresUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PostgresUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state PostgresUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "role": resources_core.FormatString(plan.Role), + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PostgresUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PostgresUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "role": resources_core.FormatString(state.Role), + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "role", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *PostgresUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/91_redis_resource.go b/generated/dev/go/91_redis_resource.go new file mode 100644 index 0000000..157507b --- /dev/null +++ b/generated/dev/go/91_redis_resource.go @@ -0,0 +1,447 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: redis +// Service ID: 91 + +var _ resource.Resource = &RedisResource{} +var _ resource.ResourceWithModifyPlan = &RedisResource{} +var _ resource.ResourceWithImportState = &RedisResource{} + +type RedisResource struct { + client *core.UniversalClient +} + +type RedisModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + IpSpaceNameSlave types.String `tfsdk:"ip_space_name_slave"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + NeedExternalAddressSlave types.Bool `tfsdk:"need_external_address_slave"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewRedisResource() resource.Resource { + return &RedisResource{} +} + +func (r *RedisResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_redis" +} + +func (r *RedisResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster Не изменяется после создания."}, + "ip_space_name_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave Не изменяется после создания."}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний IP для доступа к master Не изменяется после создания."}, + "need_external_address_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Квота ядра пода (Указывается в mili) Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *RedisResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *RedisModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *RedisModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan RedisModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить ip_space_name_master", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.IpSpaceNameSlave.IsNull() && !plan.IpSpaceNameSlave.IsUnknown() && !state.IpSpaceNameSlave.IsNull() && !state.IpSpaceNameSlave.IsUnknown() { + if plan.IpSpaceNameSlave.ValueString() != state.IpSpaceNameSlave.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить ip_space_name_slave", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_master", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressSlave.IsNull() && !plan.NeedExternalAddressSlave.IsUnknown() && !state.NeedExternalAddressSlave.IsNull() && !state.NeedExternalAddressSlave.IsUnknown() { + if plan.NeedExternalAddressSlave.ValueBool() != state.NeedExternalAddressSlave.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_slave", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 112: resources_core.FormatInt64(config.ResourceCPU), + 113: resources_core.FormatInt64(config.ResourceMemory), + 114: resources_core.FormatInt64(config.ResourceDisk), + 181: resources_core.FormatInt64(config.ResourceInstances), + 182: resources_core.FormatString(config.ResourceRealm), + 183: resources_core.FormatBool(config.NeedExternalAddressMaster), + 184: resources_core.FormatBool(config.NeedExternalAddressSlave), + 548: resources_core.FormatString(config.IpSpaceNameMaster), + 549: resources_core.FormatString(config.IpSpaceNameSlave), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 91, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *RedisResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data RedisModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 112: resources_core.FormatInt64(data.ResourceCPU), + 113: resources_core.FormatInt64(data.ResourceMemory), + 114: resources_core.FormatInt64(data.ResourceDisk), + 181: resources_core.FormatInt64(data.ResourceInstances), + 182: resources_core.FormatString(data.ResourceRealm), + 183: resources_core.FormatBool(data.NeedExternalAddressMaster), + 184: resources_core.FormatBool(data.NeedExternalAddressSlave), + 548: resources_core.FormatString(data.IpSpaceNameMaster), + 549: resources_core.FormatString(data.IpSpaceNameSlave), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 91, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RedisResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state RedisModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 91, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan RedisModel + var state RedisModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 91, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RedisResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state RedisModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *RedisResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *RedisResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/92_mongodb_resource.go b/generated/dev/go/92_mongodb_resource.go new file mode 100644 index 0000000..1e1ccaa --- /dev/null +++ b/generated/dev/go/92_mongodb_resource.go @@ -0,0 +1,480 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mongodb +// Service ID: 92 + +var _ resource.Resource = &MongodbResource{} +var _ resource.ResourceWithModifyPlan = &MongodbResource{} +var _ resource.ResourceWithImportState = &MongodbResource{} + +type MongodbResource struct { + client *core.UniversalClient +} + +type MongodbModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewMongodbResource() resource.Resource { + return &MongodbResource{} +} + +func (r *MongodbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mongodb" +} + +func (r *MongodbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`"}, + "resource_c_p_u": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `1000`"}, + "resource_disk": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `20`"}, + "resource_instances": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2`"}, + "resource_memory": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2048`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MongodbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *MongodbModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *MongodbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan MongodbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceCPU.IsNull() || config.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if config.ResourceDisk.IsNull() || config.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if config.ResourceInstances.IsNull() || config.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if config.ResourceMemory.IsNull() || config.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 487: resources_core.FormatInt64(config.ResourceInstances), + 588: resources_core.FormatInt64(config.ResourceMemory), + 589: resources_core.FormatInt64(config.ResourceCPU), + 590: resources_core.FormatInt64(config.ResourceDisk), + 595: resources_core.FormatString(config.ResourceRealm), + 645: resources_core.FormatBool(config.NeedExternalAddressMaster), + 646: resources_core.FormatString(config.IpSpaceNameMaster), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 92, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *MongodbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data MongodbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceCPU.IsNull() || data.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if data.ResourceDisk.IsNull() || data.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if data.ResourceInstances.IsNull() || data.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if data.ResourceMemory.IsNull() || data.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 487: resources_core.FormatInt64(data.ResourceInstances), + 588: resources_core.FormatInt64(data.ResourceMemory), + 589: resources_core.FormatInt64(data.ResourceCPU), + 590: resources_core.FormatInt64(data.ResourceDisk), + 595: resources_core.FormatString(data.ResourceRealm), + 645: resources_core.FormatBool(data.NeedExternalAddressMaster), + 646: resources_core.FormatString(data.IpSpaceNameMaster), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 92, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MongodbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MongodbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 92, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MongodbModel + var state MongodbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 648: resources_core.FormatInt64(plan.ResourceInstances), + 649: resources_core.FormatInt64(plan.ResourceMemory), + 650: resources_core.FormatInt64(plan.ResourceCPU), + 651: resources_core.FormatInt64(plan.ResourceDisk), + 652: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 653: resources_core.FormatString(plan.IpSpaceNameMaster), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 92, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MongodbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MongodbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *MongodbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *MongodbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/92_mongodb_user_resource.go b/generated/dev/go/92_mongodb_user_resource.go new file mode 100644 index 0000000..cdd8684 --- /dev/null +++ b/generated/dev/go/92_mongodb_user_resource.go @@ -0,0 +1,368 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mongodb +// Subresource: user + +var _ resource.Resource = &MongodbUserResource{} + +// MongodbUserResource manages a subresource via operations. +type MongodbUserResource struct { + client *core.UniversalClient +} + +type MongodbUserModel struct { + ID types.String `tfsdk:"id"` + MongodbID types.String `tfsdk:"mongodb_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + DbName types.String `tfsdk:"db_name"` + Role types.String `tfsdk:"role"` + Username types.String `tfsdk:"username"` +} + +func NewMongodbUserResource() resource.Resource { + return &MongodbUserResource{} +} + +func (r *MongodbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mongodb_user" +} + +func (r *MongodbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "mongodb_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "db_name": schema.StringAttribute{Required: true}, + "role": schema.StringAttribute{Required: true}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MongodbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan MongodbUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.DbName.IsNull() || plan.DbName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") + return + } + if plan.Role.IsNull() || plan.Role.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "role is required.") + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "dbName": resources_core.FormatString(plan.DbName), + "role": resources_core.FormatString(plan.Role), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MongodbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MongodbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MongodbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MongodbUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "modify_user" == "" { + var state MongodbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "dbName": resources_core.FormatString(plan.DbName), + "role": resources_core.FormatString(plan.Role), + }) + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MongodbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MongodbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.MongodbID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *MongodbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/93_rabbitmq_resource.go b/generated/dev/go/93_rabbitmq_resource.go new file mode 100644 index 0000000..87d44f2 --- /dev/null +++ b/generated/dev/go/93_rabbitmq_resource.go @@ -0,0 +1,449 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: rabbitmq +// Service ID: 93 + +var _ resource.Resource = &RabbitmqResource{} +var _ resource.ResourceWithModifyPlan = &RabbitmqResource{} +var _ resource.ResourceWithImportState = &RabbitmqResource{} + +type RabbitmqResource struct { + client *core.UniversalClient +} + +type RabbitmqModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewRabbitmqResource() resource.Resource { + return &RabbitmqResource{} +} + +func (r *RabbitmqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_rabbitmq" +} + +func (r *RabbitmqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `2000`"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Пример: `20`"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1124), MarkdownDescription: "Пример: `2048`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *RabbitmqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *RabbitmqModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *RabbitmqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan RabbitmqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 116: resources_core.FormatInt64(config.ResourceCPU), + 117: resources_core.FormatInt64(config.ResourceMemory), + 118: resources_core.FormatInt64(config.ResourceDisk), + 119: resources_core.FormatString(config.ResourceRealm), + 224: resources_core.FormatInt64(config.ResourceInstances), + 225: resources_core.FormatBool(config.NeedExternalAddressMaster), + 543: resources_core.FormatString(config.IpSpaceNameMaster), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 93, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *RabbitmqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data RabbitmqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 116: resources_core.FormatInt64(data.ResourceCPU), + 117: resources_core.FormatInt64(data.ResourceMemory), + 118: resources_core.FormatInt64(data.ResourceDisk), + 119: resources_core.FormatString(data.ResourceRealm), + 224: resources_core.FormatInt64(data.ResourceInstances), + 225: resources_core.FormatBool(data.NeedExternalAddressMaster), + 543: resources_core.FormatString(data.IpSpaceNameMaster), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 93, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RabbitmqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state RabbitmqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 93, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan RabbitmqModel + var state RabbitmqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 304: resources_core.FormatInt64(plan.ResourceCPU), + 305: resources_core.FormatInt64(plan.ResourceMemory), + 306: resources_core.FormatInt64(plan.ResourceDisk), + 307: resources_core.FormatInt64(plan.ResourceInstances), + 308: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 545: resources_core.FormatString(plan.IpSpaceNameMaster), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 93, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RabbitmqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state RabbitmqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *RabbitmqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *RabbitmqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/94_lucee_redeploy_action.go b/generated/dev/go/94_lucee_redeploy_action.go similarity index 91% rename from provider/internal/resources_gen/94_lucee_redeploy_action.go rename to generated/dev/go/94_lucee_redeploy_action.go index 4cfe52f..d9bce0b 100644 --- a/provider/internal/resources_gen/94_lucee_redeploy_action.go +++ b/generated/dev/go/94_lucee_redeploy_action.go @@ -31,7 +31,7 @@ type LuceeRedeployModel struct { AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` + ResourceRealm types.String `tfsdk:"resource_realm"` } func NewLuceeRedeployResource() resource.Resource { @@ -50,7 +50,7 @@ func (r *LuceeRedeployResource) Schema(ctx context.Context, req resource.SchemaR "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, } resp.Schema = schema.Schema{Attributes: attrs} } @@ -61,8 +61,8 @@ func (r *LuceeRedeployResource) Create(ctx context.Context, req resource.CreateR if resp.Diagnostics.HasError() { return } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return } @@ -74,7 +74,7 @@ func (r *LuceeRedeployResource) Create(ctx context.Context, req resource.CreateR } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } operationTimeout := "" @@ -117,7 +117,7 @@ func (r *LuceeRedeployResource) Update(ctx context.Context, req resource.UpdateR } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { diff --git a/generated/dev/go/94_lucee_resource.go b/generated/dev/go/94_lucee_resource.go new file mode 100644 index 0000000..30f3ffd --- /dev/null +++ b/generated/dev/go/94_lucee_resource.go @@ -0,0 +1,487 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: lucee +// Service ID: 94 + +var _ resource.Resource = &LuceeResource{} +var _ resource.ResourceWithModifyPlan = &LuceeResource{} +var _ resource.ResourceWithImportState = &LuceeResource{} + +type LuceeResource struct { + client *core.UniversalClient +} + +type LuceeModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AppVersion types.String `tfsdk:"app_version"` + Domain types.String `tfsdk:"domain"` + GitPath types.String `tfsdk:"git_path"` + HealthPath types.String `tfsdk:"health_path"` + JsonEnv types.String `tfsdk:"json_env"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewLuceeResource() resource.Resource { + return &LuceeResource{} +} + +func (r *LuceeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_lucee" +} + +func (r *LuceeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5.4"), MarkdownDescription: "Версия Lucee"}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lucee-"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до Git, который будет webroot для сайта"}, + "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код"}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{ \"param\": \"value\" }"), MarkdownDescription: "Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *LuceeResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *LuceeModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *LuceeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan LuceeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 103: resources_core.FormatString(config.Domain), + 104: resources_core.FormatString(config.GitPath), + 105: resources_core.FormatString(config.JsonEnv), + 106: resources_core.FormatInt64(config.ResourceCPU), + 107: resources_core.FormatInt64(config.ResourceMemory), + 108: resources_core.FormatString(config.ResourceRealm), + 132: resources_core.FormatString(config.HealthPath), + 139: resources_core.FormatInt64(config.ResourceInstances), + 263: resources_core.FormatString(config.AppVersion), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 94, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *LuceeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data LuceeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 103: resources_core.FormatString(data.Domain), + 104: resources_core.FormatString(data.GitPath), + 105: resources_core.FormatString(data.JsonEnv), + 106: resources_core.FormatInt64(data.ResourceCPU), + 107: resources_core.FormatInt64(data.ResourceMemory), + 108: resources_core.FormatString(data.ResourceRealm), + 132: resources_core.FormatString(data.HealthPath), + 139: resources_core.FormatInt64(data.ResourceInstances), + 263: resources_core.FormatString(data.AppVersion), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 94, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *LuceeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state LuceeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 94, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan LuceeModel + var state LuceeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.GitPath.IsNull() != state.GitPath.IsNull() || plan.GitPath.IsUnknown() != state.GitPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.GitPath.IsNull() && !plan.GitPath.IsUnknown() && !state.GitPath.IsNull() && !state.GitPath.IsUnknown() { + if plan.GitPath.ValueString() != state.GitPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.HealthPath.IsNull() != state.HealthPath.IsNull() || plan.HealthPath.IsUnknown() != state.HealthPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.HealthPath.IsNull() && !plan.HealthPath.IsUnknown() && !state.HealthPath.IsNull() && !state.HealthPath.IsUnknown() { + if plan.HealthPath.ValueString() != state.HealthPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 133: resources_core.FormatString(plan.GitPath), + 134: resources_core.FormatString(plan.HealthPath), + 135: resources_core.FormatString(plan.JsonEnv), + 136: resources_core.FormatInt64(plan.ResourceCPU), + 137: resources_core.FormatInt64(plan.ResourceMemory), + 138: resources_core.FormatInt64(plan.ResourceInstances), + 264: resources_core.FormatString(plan.AppVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 94, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *LuceeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state LuceeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *LuceeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *LuceeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/94_lucee_restart_action.go b/generated/dev/go/94_lucee_restart_action.go similarity index 100% rename from provider/internal/resources_gen/94_lucee_restart_action.go rename to generated/dev/go/94_lucee_restart_action.go diff --git a/provider/internal/resources_gen/95_nodejs_redeploy_action.go b/generated/dev/go/95_nodejs_redeploy_action.go similarity index 91% rename from provider/internal/resources_gen/95_nodejs_redeploy_action.go rename to generated/dev/go/95_nodejs_redeploy_action.go index d767ed5..6d884ea 100644 --- a/provider/internal/resources_gen/95_nodejs_redeploy_action.go +++ b/generated/dev/go/95_nodejs_redeploy_action.go @@ -31,7 +31,7 @@ type NodejsRedeployModel struct { AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` + ResourceRealm types.String `tfsdk:"resource_realm"` } func NewNodejsRedeployResource() resource.Resource { @@ -50,7 +50,7 @@ func (r *NodejsRedeployResource) Schema(ctx context.Context, req resource.Schema "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, } resp.Schema = schema.Schema{Attributes: attrs} } @@ -61,8 +61,8 @@ func (r *NodejsRedeployResource) Create(ctx context.Context, req resource.Create if resp.Diagnostics.HasError() { return } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return } @@ -74,7 +74,7 @@ func (r *NodejsRedeployResource) Create(ctx context.Context, req resource.Create } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } operationTimeout := "" @@ -117,7 +117,7 @@ func (r *NodejsRedeployResource) Update(ctx context.Context, req resource.Update } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { diff --git a/generated/dev/go/95_nodejs_resource.go b/generated/dev/go/95_nodejs_resource.go new file mode 100644 index 0000000..37b2799 --- /dev/null +++ b/generated/dev/go/95_nodejs_resource.go @@ -0,0 +1,487 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nodejs +// Service ID: 95 + +var _ resource.Resource = &NodejsResource{} +var _ resource.ResourceWithModifyPlan = &NodejsResource{} +var _ resource.ResourceWithImportState = &NodejsResource{} + +type NodejsResource struct { + client *core.UniversalClient +} + +type NodejsModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + AppVersion types.String `tfsdk:"app_version"` + Domain types.String `tfsdk:"domain"` + GitPath types.String `tfsdk:"git_path"` + HealthPath types.String `tfsdk:"health_path"` + JsonEnv types.String `tfsdk:"json_env"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNodejsResource() resource.Resource { + return &NodejsResource{} +} + +func (r *NodejsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nodejs" +} + +func (r *NodejsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("23"), MarkdownDescription: "Версия приложения"}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testnode.git"), MarkdownDescription: "Путь до Git, который будет webroot для сайта"}, + "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет"}, + "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NodejsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NodejsModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NodejsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NodejsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 149: resources_core.FormatString(config.Domain), + 150: resources_core.FormatString(config.GitPath), + 151: resources_core.FormatString(config.HealthPath), + 152: resources_core.FormatString(config.JsonEnv), + 153: resources_core.FormatInt64(config.ResourceCPU), + 154: resources_core.FormatInt64(config.ResourceMemory), + 155: resources_core.FormatInt64(config.ResourceInstances), + 156: resources_core.FormatString(config.ResourceRealm), + 270: resources_core.FormatString(config.AppVersion), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 95, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NodejsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NodejsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 149: resources_core.FormatString(data.Domain), + 150: resources_core.FormatString(data.GitPath), + 151: resources_core.FormatString(data.HealthPath), + 152: resources_core.FormatString(data.JsonEnv), + 153: resources_core.FormatInt64(data.ResourceCPU), + 154: resources_core.FormatInt64(data.ResourceMemory), + 155: resources_core.FormatInt64(data.ResourceInstances), + 156: resources_core.FormatString(data.ResourceRealm), + 270: resources_core.FormatString(data.AppVersion), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 95, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NodejsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NodejsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 95, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NodejsModel + var state NodejsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.GitPath.IsNull() != state.GitPath.IsNull() || plan.GitPath.IsUnknown() != state.GitPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.GitPath.IsNull() && !plan.GitPath.IsUnknown() && !state.GitPath.IsNull() && !state.GitPath.IsUnknown() { + if plan.GitPath.ValueString() != state.GitPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.HealthPath.IsNull() != state.HealthPath.IsNull() || plan.HealthPath.IsUnknown() != state.HealthPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.HealthPath.IsNull() && !plan.HealthPath.IsUnknown() && !state.HealthPath.IsNull() && !state.HealthPath.IsUnknown() { + if plan.HealthPath.ValueString() != state.HealthPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 157: resources_core.FormatString(plan.GitPath), + 158: resources_core.FormatString(plan.HealthPath), + 159: resources_core.FormatString(plan.JsonEnv), + 160: resources_core.FormatInt64(plan.ResourceCPU), + 161: resources_core.FormatInt64(plan.ResourceMemory), + 162: resources_core.FormatInt64(plan.ResourceInstances), + 271: resources_core.FormatString(plan.AppVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 95, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NodejsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NodejsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NodejsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NodejsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/96_pgadmin_redeploy_action.go b/generated/dev/go/96_pgadmin_redeploy_action.go similarity index 100% rename from provider/internal/resources_gen/96_pgadmin_redeploy_action.go rename to generated/dev/go/96_pgadmin_redeploy_action.go diff --git a/provider/internal/resources_gen/96_pgadmin_resource.go b/generated/dev/go/96_pgadmin_resource.go similarity index 89% rename from provider/internal/resources_gen/96_pgadmin_resource.go rename to generated/dev/go/96_pgadmin_resource.go index dd1136e..e7556b6 100644 --- a/provider/internal/resources_gen/96_pgadmin_resource.go +++ b/generated/dev/go/96_pgadmin_resource.go @@ -2,7 +2,6 @@ package resources_gen import ( "context" - "strings" "terraform-provider-nubes/internal/core" "terraform-provider-nubes/internal/resources_core" @@ -12,9 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -64,7 +61,7 @@ func (r *PgadminResource) Metadata(ctx context.Context, req resource.MetadataReq func (r *PgadminResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "id": schema.StringAttribute{Computed: true}, "resource_name": schema.StringAttribute{Required: true}, "operation_timeout": schema.StringAttribute{Optional: true}, "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, @@ -126,64 +123,43 @@ func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPla } } if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { + if plan.Domain.ValueString() != state.Domain.ValueString() { resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") return } } if !plan.Login.IsNull() && !plan.Login.IsUnknown() && !state.Login.IsNull() && !state.Login.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Login.ValueString(), state.Login.ValueString()) { + if plan.Login.ValueString() != state.Login.ValueString() { resp.Diagnostics.AddError("Нельзя изменить login", "Параметр задается при создании и не может быть изменен.") return } } if !plan.Password.IsNull() && !plan.Password.IsUnknown() && !state.Password.IsNull() && !state.Password.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Password.ValueString(), state.Password.ValueString()) { + if plan.Password.ValueString() != state.Password.ValueString() { resp.Diagnostics.AddError("Нельзя изменить password", "Параметр задается при создании и не может быть изменен.") return } } if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") return } } return } - if config.Login.IsNull() { + if config.Login.IsNull() || config.Login.IsUnknown() { resp.Diagnostics.AddError("Missing required attribute", "login is required.") return } - if config.Login.IsUnknown() { - return - } - if config.Password.IsNull() { + if config.Password.IsNull() || config.Password.IsUnknown() { resp.Diagnostics.AddError("Missing required attribute", "password is required.") return } - if config.Password.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return } - if config.ResourceRealm.IsUnknown() { - return - } if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { return @@ -205,7 +181,7 @@ func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPla if !config.Domain.IsNull() && !config.Domain.IsUnknown() { desiredDomain = config.Domain.ValueString() } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 96, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) } @@ -234,7 +210,7 @@ func (r *PgadminResource) Create(ctx context.Context, req resource.CreateRequest if !data.Domain.IsNull() && !data.Domain.IsUnknown() { desiredDomain = data.Domain.ValueString() } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) params := map[int]string{ diff --git a/generated/dev/go/97_nodered_resource.go b/generated/dev/go/97_nodered_resource.go new file mode 100644 index 0000000..26074c9 --- /dev/null +++ b/generated/dev/go/97_nodered_resource.go @@ -0,0 +1,422 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nodered +// Service ID: 97 + +var _ resource.Resource = &NoderedResource{} +var _ resource.ResourceWithModifyPlan = &NoderedResource{} +var _ resource.ResourceWithImportState = &NoderedResource{} + +type NoderedResource struct { + client *core.UniversalClient +} + +type NoderedModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNoderedResource() resource.Resource { + return &NoderedResource{} +} + +func (r *NoderedResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nodered" +} + +func (r *NoderedResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NoderedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NoderedModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NoderedModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NoderedModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 193: resources_core.FormatInt64(config.ResourceCPU), + 194: resources_core.FormatInt64(config.ResourceMemory), + 195: resources_core.FormatInt64(config.ResourceDisk), + 196: resources_core.FormatString(config.ResourceRealm), + 197: resources_core.FormatString(config.Domain), + 323: resources_core.FormatInt64(config.ResourceInstances), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 97, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NoderedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NoderedModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Domain.IsNull() || data.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 193: resources_core.FormatInt64(data.ResourceCPU), + 194: resources_core.FormatInt64(data.ResourceMemory), + 195: resources_core.FormatInt64(data.ResourceDisk), + 196: resources_core.FormatString(data.ResourceRealm), + 197: resources_core.FormatString(data.Domain), + 323: resources_core.FormatInt64(data.ResourceInstances), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 97, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NoderedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NoderedModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 97, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NoderedModel + var state NoderedModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 97, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NoderedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NoderedModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NoderedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NoderedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/98_http_redeploy_action.go b/generated/dev/go/98_http_redeploy_action.go similarity index 100% rename from provider/internal/resources_gen/98_http_redeploy_action.go rename to generated/dev/go/98_http_redeploy_action.go diff --git a/provider/internal/resources_gen/98_http_resource.go b/generated/dev/go/98_http_resource.go similarity index 83% rename from provider/internal/resources_gen/98_http_resource.go rename to generated/dev/go/98_http_resource.go index 1c7a5fb..5750298 100644 --- a/provider/internal/resources_gen/98_http_resource.go +++ b/generated/dev/go/98_http_resource.go @@ -2,7 +2,6 @@ package resources_gen import ( "context" - "strings" "terraform-provider-nubes/internal/core" "terraform-provider-nubes/internal/resources_core" @@ -14,7 +13,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -36,7 +34,6 @@ type HttpModel struct { OperationTimeout types.String `tfsdk:"operation_timeout"` LogLevel types.String `tfsdk:"log_level"` Domain types.String `tfsdk:"domain"` - IngressAnnotations types.String `tfsdk:"ingress_annotations"` JsonEnv types.String `tfsdk:"json_env"` RegistryPath types.String `tfsdk:"registry_path"` ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` @@ -65,13 +62,12 @@ func (r *HttpResource) Metadata(ctx context.Context, req resource.MetadataReques func (r *HttpResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "id": schema.StringAttribute{Computed: true}, "resource_name": schema.StringAttribute{Required: true}, "operation_timeout": schema.StringAttribute{Optional: true}, "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_annotations": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Формат ключ/значение
Поддерживает пока простые аннотации Не изменяется после создания."}, - "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания."}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{"param": "value"}"), MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, "registry_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nexus-sa.tst.nubes.ru/docker-nubes/jolt"), MarkdownDescription: "Путь до image, который будет установлен как deployment Не изменяется после создания."}, "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili)"}, "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, @@ -128,59 +124,35 @@ func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRe } } if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { + if plan.Domain.ValueString() != state.Domain.ValueString() { resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") return } } - if !plan.IngressAnnotations.IsNull() && !plan.IngressAnnotations.IsUnknown() && !state.IngressAnnotations.IsNull() && !state.IngressAnnotations.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IngressAnnotations.ValueString(), state.IngressAnnotations.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ingress_annotations", "Параметр задается при создании и не может быть изменен.") - return - } - } if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { resp.Diagnostics.AddError("Нельзя изменить json_env", "Параметр задается при создании и не может быть изменен.") return } } if !plan.RegistryPath.IsNull() && !plan.RegistryPath.IsUnknown() && !state.RegistryPath.IsNull() && !state.RegistryPath.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RegistryPath.ValueString(), state.RegistryPath.ValueString()) { + if plan.RegistryPath.ValueString() != state.RegistryPath.ValueString() { resp.Diagnostics.AddError("Нельзя изменить registry_path", "Параметр задается при создании и не может быть изменен.") return } } if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") return } } return } - if config.ResourceRealm.IsNull() { + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return } - if config.ResourceRealm.IsUnknown() { - return - } if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { return @@ -197,13 +169,12 @@ func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRe 216: resources_core.FormatString(config.ResourceRealm), 217: resources_core.FormatString(config.RegistryPath), 241: resources_core.FormatString(config.JsonEnv), - 782: resources_core.FormatString(config.IngressAnnotations), } desiredDomain := "" if !config.Domain.IsNull() && !config.Domain.IsUnknown() { desiredDomain = config.Domain.ValueString() } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 98, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) } @@ -224,7 +195,7 @@ func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, r if !data.Domain.IsNull() && !data.Domain.IsUnknown() { desiredDomain = data.Domain.ValueString() } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) params := map[int]string{ @@ -235,7 +206,6 @@ func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, r 216: resources_core.FormatString(data.ResourceRealm), 217: resources_core.FormatString(data.RegistryPath), 241: resources_core.FormatString(data.JsonEnv), - 782: resources_core.FormatString(data.IngressAnnotations), } operationTimeout := "" @@ -263,7 +233,6 @@ func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, r {Code: "vault_fields"}, }, []resources_core.InputField{ {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, {Code: "registryPath", Field: "RegistryPath", Type: "string"}, {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, @@ -311,7 +280,6 @@ func (r *HttpResource) Read(ctx context.Context, req resource.ReadRequest, resp {Code: "vault_fields"}, }, []resources_core.InputField{ {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, {Code: "registryPath", Field: "RegistryPath", Type: "string"}, {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, @@ -418,7 +386,6 @@ func (r *HttpResource) Update(ctx context.Context, req resource.UpdateRequest, r {Code: "vault_fields"}, }, []resources_core.InputField{ {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, {Code: "registryPath", Field: "RegistryPath", Type: "string"}, {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, diff --git a/generated/dev/go/99_gitea_resource.go b/generated/dev/go/99_gitea_resource.go new file mode 100644 index 0000000..86e1830 --- /dev/null +++ b/generated/dev/go/99_gitea_resource.go @@ -0,0 +1,493 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: gitea +// Service ID: 99 + +var _ resource.Resource = &GiteaResource{} +var _ resource.ResourceWithModifyPlan = &GiteaResource{} +var _ resource.ResourceWithImportState = &GiteaResource{} + +type GiteaResource struct { + client *core.UniversalClient +} + +type GiteaModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + Domain types.String `tfsdk:"domain"` + PsqlUid types.String `tfsdk:"psql_uid"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewGiteaResource() resource.Resource { + return &GiteaResource{} +} + +func (r *GiteaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_gitea" +} + +func (r *GiteaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *GiteaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *GiteaModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *GiteaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan GiteaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, plan.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != plan.PsqlUid.ValueString() { + plan.PsqlUid = types.StringValue(resolvedPsqlUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + if plan.PsqlUid.ValueString() != state.PsqlUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.PsqlUid.IsNull() || config.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { + config.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + params := map[int]string{ + 232: resources_core.FormatInt64(config.ResourceCPU), + 233: resources_core.FormatInt64(config.ResourceMemory), + 234: resources_core.FormatInt64(config.ResourceDisk), + 235: resources_core.FormatInt64(config.ResourceInstances), + 237: resources_core.FormatString(config.Domain), + 238: resources_core.FormatString(config.PsqlUid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 99, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *GiteaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data GiteaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { + data.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149, 151, 153} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 232: resources_core.FormatInt64(data.ResourceCPU), + 233: resources_core.FormatInt64(data.ResourceMemory), + 234: resources_core.FormatInt64(data.ResourceDisk), + 235: resources_core.FormatInt64(data.ResourceInstances), + 237: resources_core.FormatString(data.Domain), + 238: resources_core.FormatString(data.PsqlUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 99, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *GiteaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state GiteaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 99, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { + newState.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan GiteaModel + var state GiteaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 259: resources_core.FormatInt64(plan.ResourceCPU), + 260: resources_core.FormatInt64(plan.ResourceMemory), + 261: resources_core.FormatInt64(plan.ResourceDisk), + 262: resources_core.FormatInt64(plan.ResourceInstances), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 99, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *GiteaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state GiteaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *GiteaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *GiteaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/dev/go/registry.go b/generated/dev/go/registry.go new file mode 100644 index 0000000..0a299b3 --- /dev/null +++ b/generated/dev/go/registry.go @@ -0,0 +1,83 @@ +package resources_gen + +import "github.com/hashicorp/terraform-plugin-framework/resource" + +// Code generated by tools/gen_v2. DO NOT EDIT. +func AllResources() []func() resource.Resource { + return []func() resource.Resource{ + NewAkhqResource, + NewClickhouseResource, + NewDnsrecordResource, + NewDnszoneResource, + NewDummyResource, + NewFlaskResource, + NewGiteaResource, + NewHarborResource, + NewHttpResource, + NewK8sOpenbaoResource, + NewK8sSthutrvalClusterResource, + NewK8sZitiControllerResource, + NewKafkaResource, + NewLuceeResource, + NewMariadbResource, + NewMongodbResource, + NewNextcloudResource, + NewNifiResource, + NewNodejsResource, + NewNoderedResource, + NewPgadminResource, + NewPostgresResource, + NewRabbitmqResource, + NewRedisResource, + NewS3Resource, + NewS3bucketResource, + NewSupersetResource, + NewTemplateResource, + NewValoTenantResource, + NewVappResource, + NewVcNsxtResource, + NewVcOrgResource, + NewVcVdcResource, + NewVcVdcGroupResource, + NewVcVmV3Resource, + NewVcexternalipResource, + NewZonesV2Resource, + NewClickhouseDatabaseResource, + NewClickhouseUserResource, + NewK8sOpenbaoKvResource, + NewK8sOpenbaoPolicyResource, + NewK8sOpenbaoSecretsResource, + NewKafkaTopicResource, + NewKafkaUserResource, + NewMariadbDatabaseResource, + NewMariadbUserResource, + NewMongodbUserResource, + NewPostgresDatabaseResource, + NewPostgresUserResource, + NewS3SubUserResource, + NewDummyReconcileResource, + NewDummyRedeployResource, + NewHttpRedeployResource, + NewK8sOpenbaoReconcileResource, + NewK8sOpenbaoRecoverySecretsResource, + NewK8sOpenbaoRestartResource, + NewK8sOpenbaoUpdatePolicyResource, + NewK8sOpenbaoUpdateSecretsResource, + NewK8sSthutrvalClusterReconcileResource, + NewK8sSthutrvalClusterUpdateSecretsResource, + NewLuceeRedeployResource, + NewLuceeRestartResource, + NewNextcloudRestartResource, + NewNodejsRedeployResource, + NewPgadminRedeployResource, + NewPostgresRecoveryResource, + NewPostgresRestartResource, + NewVcNsxtReconcileResource, + NewVcOrgReconcileResource, + NewVcVdcReconcileResource, + NewVcVdcGroupAddVdcResource, + NewVcVdcGroupReconcileResource, + NewVcVdcGroupRemoveVdcResource, + NewVcVmV3RedeployResource, + } +} diff --git a/generated/dev/json/.gitkeep b/generated/dev/json/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/dev/resources_yaml/.gitkeep b/generated/dev/resources_yaml/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/dev/resources_yaml/109_zones_v2.yaml b/generated/dev/resources_yaml/109_zones_v2.yaml new file mode 100644 index 0000000..d75fa9e --- /dev/null +++ b/generated/dev/resources_yaml/109_zones_v2.yaml @@ -0,0 +1,111 @@ +name: zones_v2 +service_id: 109 +service_display_name: Управление днс-зонами и записями +service_short_name: zonesV2 +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 254 + kind: instance + action: create + params: + - id: 761 + code: zoneName + data_type: string + required: true + regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ + maxlength: 64 + minlength: 1 + descr: Имя зоны + sort: 20 + - id: 762 + code: zoneKind + data_type: string + required: true + default: NATIVE + value_list: + - NATIVE + - SLAVE + descr: Тип зоны + sort: 30 + - id: 763 + code: zoneMastersForSlave + data_type: string + required: false + descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов + man: Пример:
**8.8.8.8 8.8.4.4** + sort: 40 + - id: 764 + code: zoneTimeToRefresh + data_type: integer > 0 + required: true + default: "86400" + minvalue: 600 + descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов + sort: 50 + - id: 765 + code: zoneTimeToRetry + data_type: integer > 0 + required: true + default: "7200" + minvalue: 600 + descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов + sort: 60 + - id: 766 + code: zoneTimeToExpire + data_type: integer > 0 + required: true + default: "3600000" + minvalue: 3600 + descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным + sort: 70 + - id: 767 + code: zoneTimeToLive + data_type: integer > 0 + required: true + default: "86400" + minvalue: 600 + descr: Время, в течение которого информация будет кешироваться другими DNS-серверами + sort: 80 + - id: 768 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + sort: 10 + - name: delete + id: 255 + kind: instance + action: delete + params: [] + - name: modify + id: 256 + kind: instance + action: modify + params: + - id: 769 + code: records + data_type: array-map-fixed + required: true + sort: 10 diff --git a/provider/resources_yaml/110_dnszone.yaml b/generated/dev/resources_yaml/110_dnszone.yaml similarity index 100% rename from provider/resources_yaml/110_dnszone.yaml rename to generated/dev/resources_yaml/110_dnszone.yaml diff --git a/generated/dev/resources_yaml/111_dnsrecord.yaml b/generated/dev/resources_yaml/111_dnsrecord.yaml new file mode 100644 index 0000000..4efad14 --- /dev/null +++ b/generated/dev/resources_yaml/111_dnsrecord.yaml @@ -0,0 +1,100 @@ +name: dnsrecord +service_id: 111 +service_display_name: DNS запись +service_short_name: dnsrecord +service_man: Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 45 + kind: instance + action: create + params: + - id: 75 + code: zoneUid + data_type: uuid + required: true + ref_svc_id: 110 + descr: UUID экземпляра услуги DNS-зоны + man: UUID экземпляра услуги DNS-зоны + sort: 10 + - id: 76 + code: recordType + data_type: string + required: true + value_list: + - A + - AAAA + - CNAME + - MX + - PTR + - SOA + - SRV + - TXT + - LUA + - NS + - SSHFP + descr: Тип DNS-записи + man: Тип DNS-записи + sort: 20 + - id: 77 + code: recordName + data_type: string + required: true + unique_scope: parent + descr: Имя записи. Имя зоны в конце проставляется автоматически + man: Имя записи. Имя зоны в конце проставляется автоматически + sort: 30 + - id: 78 + code: recordInput + data_type: string + required: true + descr: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP + man: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP + sort: 40 + is_modifiable: true + - id: 450 + code: recordTTL + data_type: integer > 0 + required: true + default: "3600" + maxvalue: 604800 + minvalue: 30 + descr: Time-to-Leave записи. Раз в какое время запись будет обновлена + sort: 50 + - name: delete + id: 46 + kind: instance + action: delete + params: [] + - name: modify + id: 90 + kind: instance + action: modify + params: + - id: 256 + code: recordInput + data_type: string + required: true + descr: Запись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP diff --git a/generated/dev/resources_yaml/115_mariadb.yaml b/generated/dev/resources_yaml/115_mariadb.yaml new file mode 100644 index 0000000..71c47df --- /dev/null +++ b/generated/dev/resources_yaml/115_mariadb.yaml @@ -0,0 +1,318 @@ +name: mariadb +service_id: 115 +service_display_name: Mariadb +service_short_name: mariadb +service_man: 'Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 138 + kind: instance + action: create + params: + - id: 422 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 10 + - id: 424 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 500 + descr: Квота ядра пода (milicores) + sort: 30 + is_modifiable: true + - id: 425 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 1024 + descr: Квота памяти пода (MBytes) + sort: 30 + is_modifiable: true + - id: 426 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 427 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 60 + is_modifiable: true + - id: 428 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к master + sort: 70 + is_modifiable: true + - id: 429 + code: ipSpaceNameMaster + data_type: string + required: false + descr: Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster + sort: 80 + is_modifiable: true + - id: 430 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + default: 0 * * * * + regex: ^(\*|(\d+|\*\/\d+|\d+(,\d+)*))\s\*\s\*\s\*\s\*$ + descr: Не обязательный. Время запуска бекапа + sort: 90 + is_modifiable: true + - id: 431 + code: appVersion + data_type: string + required: true + default: 9.4.0 + value_list: + - 8.4.0 + - 9.4.0 + descr: Версия MySQL + sort: 100 + - id: 432 + code: autoScale + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + sort: 310 + is_modifiable: true + - id: 433 + code: autoScalePercentage + data_type: integer > 0 + required: false + default: "10" + value_list: + - "10" + - "15" + - "20" + descr: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + sort: 320 + is_modifiable: true + - id: 434 + code: autoScaleTechWindow + data_type: integer >= 0 + required: false + default: "0" + value_list: + - "0" + - "5" + - "12" + - "23" + descr: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + sort: 330 + is_modifiable: true + - id: 435 + code: autoScaleQuotaGb + data_type: integer > 0 + required: false + default: "1" + descr: Квота для autoScale + sort: 340 + is_modifiable: true + - id: 459 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + sort: 20 + - name: create_database + id: 242 + kind: subresource + action: create + subresource: database + params: + - id: 734 + code: databaseName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + descr: 'Пример: `db01`' + - name: create_user + id: 243 + kind: subresource + action: create + subresource: user + params: + - id: 735 + code: username + data_type: string + required: true + regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ + maxlength: 62 + minlength: 2 + descr: 'Пример: `myusername`' + sort: 10 + - id: 736 + code: password + data_type: string + required: true + maxlength: 62 + minlength: 10 + descr: Секрет будет записан в хранилище секретов и не будет отображаться публично + sort: 20 + is_sensitive: true + - id: 737 + code: databaseName + data_type: array-map-fixed + required: true + descr: Необходимо указать массив существующих баз + man: Если базы не существует - при выполнении операции вернётся ошибка + sort: 30 + - id: 738 + code: accessHosts + data_type: string + required: true + descr: 'Пример: `%`' + sort: 40 + - name: delete + id: 139 + kind: instance + action: delete + params: + - id: 456 + code: deleteS3Bucket + data_type: boolean + required: true + default: "true" + value_list: + - "false" + - "true" + descr: Также удалить технический S3 Bucket с бэкапами? + sort: 10 + - name: delete_database + id: 248 + kind: subresource + action: delete + subresource: database + params: + - id: 745 + code: databaseName + data_type: string + required: true + descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ + - name: delete_user + id: 247 + kind: subresource + action: delete + subresource: user + params: + - id: 744 + code: username + data_type: string + required: true + descr: Имя пользователя, созданное через операцию `create_user` + - name: modify + id: 142 + kind: instance + action: modify + params: + - id: 436 + code: resourceCPU + data_type: integer > 0 + required: false + - id: 437 + code: resourceMemory + data_type: integer > 0 + required: false + - id: 438 + code: resourceInstances + data_type: integer > 0 + required: false + - id: 439 + code: resourceDisk + data_type: integer > 0 + required: false + - id: 440 + code: needExternalAddressMaster + data_type: boolean + required: false + value_list: + - "false" + - "true" + - id: 441 + code: ipSpaceNameMaster + required: false + - id: 442 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + - id: 443 + code: autoScale + data_type: boolean + required: false + value_list: + - "false" + - "true" + - id: 444 + code: autoScalePercentage + data_type: integer > 0 + required: false + - id: 445 + code: autoScaleTechWindow + data_type: integer >= 0 + required: false + - id: 446 + code: autoScaleQuotaGb + data_type: integer > 0 + required: false + - name: resume + id: 141 + kind: instance + action: resume + params: [] + - name: suspend + id: 140 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/116_kafka.yaml b/generated/dev/resources_yaml/116_kafka.yaml similarity index 100% rename from provider/resources_yaml/116_kafka.yaml rename to generated/dev/resources_yaml/116_kafka.yaml diff --git a/provider/resources_yaml/119_akhq.yaml b/generated/dev/resources_yaml/119_akhq.yaml similarity index 100% rename from provider/resources_yaml/119_akhq.yaml rename to generated/dev/resources_yaml/119_akhq.yaml diff --git a/generated/dev/resources_yaml/120_clickhouse.yaml b/generated/dev/resources_yaml/120_clickhouse.yaml new file mode 100644 index 0000000..efae3cf --- /dev/null +++ b/generated/dev/resources_yaml/120_clickhouse.yaml @@ -0,0 +1,417 @@ +name: clickhouse +service_id: 120 +service_display_name: ClickHouse +service_short_name: clickhouse +service_man: '# Инструкция по управлению кластерами и пользователями ClickHouse

---

## Общая информация

ClickHouse — это колоночная система управления базами данных, предназначенная для обработки аналитических запросов в реальном времени. Платформа позволяет создавать, удалять, приостанавливать и возобновлять кластеры ClickHouse, а также управлять базами данных и пользователями.

Кластер создаётся через оператор Altinity (`https://github.com/Altinity/clickhouse-operator`) в ресурсной платформе типа `k8s`

Кластеры могут создаваться шардированными с произвольным количеством реплик. Для обеспечения отказоустойчивости и координации обязательно включение параметра `Создать ClickHouse-Keeper`.

Кластера создаются с обязательным бекапом. Для этого необходимо предварительно создать услугу `S3 Object Storage`.
---

## Доступные операции

* **create** — Создание кластера
* **delete** — Удаление кластера
* **suspend** — Приостановка работы кластера
* **resume** — Возобновление работы кластера
* **create_database** — Создание базы данных
* **delete_database** — Удаление базы данных
* **create_user** — Создание пользователя и настройка прав доступа к базе данных
* **delete_user** — Удаление пользователя

## Процесс создания и первого подключения

1. Выполнить операцию `create` - будет создан кластер
2. Выполнить операцию `create_database` - будет создана база данных
3. Выполнить операцию `create_user` - будет создан пользователь (выбрать доступ подключения к БД, созданной в п.2)

## Примеры подключения

### Подключение через HTTP-интерфейс

```bash
# Проверка доступности кластера
curl "http://<username>:<password>@<host>:8123/" -d "SELECT 1"

# Выполнение запроса
curl "http://<username>:<password>@<host>:8123/" -d "SHOW DATABASES"
```

## Простые кейсы
*Создать БД и обычной таблицы*
```clickhouse
CREATE TABLE database.table
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

INSERT INTO database.table SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM database.table;
```
Базы и таблицы не реплицируются без специального ключа (`ON CLUSTER ''clickhousek8s''`)
В случае создания в стиле, описанным выше, необходимо будет выполнять действия на всех шардах/репликах


*Создание БД и шардированной таблицы*
```clickhouse
CREATE TABLE database.localUsers
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

CREATE TABLE database.shardedTable
AS localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', id);

INSERT INTO shardedTable SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM shardedTable;
```
Важный момент - в данном примере таблицы придётся также выполнять на всех нодах ClickHouse

*Создание реплицируемой и шардированной таблицы*
```clickhouse
CREATE TABLE IF NOT EXISTS database.localUsers ON cluster ''clickhousek8s''
(
user_id UInt64,
name String,
email String
)
ENGINE = ReplicatedMergeTree(
''/clickhouse/tables/{shard}/localUsers'',
''{replica}''
)
ORDER BY user_id;

CREATE TABLE IF NOT EXISTS database.users ON cluster ''clickhousek8s''
AS database.localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', rand());

INSERT INTO database.users VALUES (1, ''Alice'', ''alice@example.com'');
INSERT INTO database.users VALUES (2, ''Bob'', ''bob@example.com'');
INSERT INTO database.users VALUES (3, ''Charlie'', ''charlie@example.com'');
INSERT INTO database.users VALUES (4, ''David'', ''david@example.com'');
INSERT INTO database.users VALUES (5, ''Gosha'', ''gosha@example.com'');
INSERT INTO database.users VALUES (6, ''Eve'', ''eve@example.com'');
INSERT INTO database.users VALUES (7, ''Frank'', ''frank@example.com'');
INSERT INTO database.users VALUES (8, ''Grace'', ''grace@example.com'');
INSERT INTO database.users VALUES (9, ''Hannah'', ''hannah@example.com'');
INSERT INTO database.users VALUES (10, ''Ivan'', ''ivan@example.com'');

SELECT * FROM database.localUsers;
SELECT * FROM database.users;

SELECT COUNT(*) AS total_users FROM database.users;
SELECT COUNT(*) AS total_users_local FROM database.localUsers;
```' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 181 + kind: instance + action: create + man: Операция создания кластера ClickHouse

Для подключения к кластеру также необходимо создать базу данных через операцию `create_database` и создание пользователя через операцию `create_user` + params: + - id: 572 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Кластер Kubernetes в котором будет развёрнуто приложение + man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы + sort: 10 + - id: 573 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + man: '**Если список не отображается**
перейдите в раздел `S3 Object Storage`
операция `create`' + sort: 20 + - id: 574 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + man: '**Для синхронизации данных между репликами** необходимо включить `Создать ClickHouse-Keeper`' + sort: 30 + is_modifiable: true + - id: 575 + code: resourceInstancesShards + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' + sort: 40 + is_modifiable: true + - id: 576 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 8000 + minvalue: 1000 + descr: 'Пример: `1000`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 50 + is_modifiable: true + - id: 577 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1536" + maxvalue: 8192 + minvalue: 1536 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 60 + is_modifiable: true + - id: 578 + code: appVersion + data_type: string + required: true + default: "25.10" + value_list: + - "25.10" + descr: Версия `clickhouse-server` + sort: 70 + is_modifiable: true + - id: 579 + code: resourceDisk + data_type: integer > 0 + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `100`' + man: Указывается в Gigabytes + sort: 80 + is_modifiable: true + - id: 580 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Позволяет подключиться извне к ClickHouse + man: Для выделения IP также необходимо указать имя Ip-space.
Его можно получить из свойств ресурсной платформы + sort: 90 + is_modifiable: true + - id: 581 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Пример: `internet-no-antiddos-v1`' + man: Имя ipSpace для публикации внешнего адреса

Не может быть пустым, если включен `Выделение белого ip для master ноды` + sort: 100 + is_modifiable: true + - id: 582 + code: needCreateKeeper + data_type: boolean + required: true + default: "true" + value_list: + - "false" + - "true" + descr: Необходим для кластеризации ClickHouse + man: Аналог `Zookeeper`, представленный от ClickHouse + sort: 200 + is_modifiable: true + - id: 583 + code: resourceCPUKeeper + data_type: integer > 0 + required: true + default: "50" + maxvalue: 8000 + minvalue: 50 + descr: 'Пример: `100`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 210 + is_modifiable: true + - id: 584 + code: resourceMemoryKeeper + data_type: integer > 0 + required: true + default: "152" + maxvalue: 8192 + minvalue: 152 + descr: 'Пример: `256`' + man: Указывается в Megabytes + sort: 220 + is_modifiable: true + - id: 585 + code: resourceInstancesKeeper + data_type: integer > 0 + required: true + default: "1" + value_list: + - "1" + - "3" + maxvalue: 3 + minvalue: 1 + descr: 'Пример: `1`' + man: Для **теста** - 1 нода.
Для **продуктива** - 3 ноды + sort: 230 + is_modifiable: true + - id: 586 + code: ext_BACKUP_SCHEDULE + data_type: string + required: true + default: 0 0 * * * + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + descr: Принимает формат `CRON` + sort: 500 + is_modifiable: true + - id: 587 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: true + default: "7" + maxvalue: 60 + minvalue: 3 + descr: 'Пример: `7`' + man: Кол-во **полных** резервных копий
Кол-во инкрементов - неограничено

Полная резервная копия выполняется раз в неделю
*Если изначально полной резервной копии нет* выполняется полный бекап
В остальные дни по крону выполняются **инкременты** + sort: 510 + is_modifiable: true + - name: create_database + id: 186 + kind: subresource + action: create + subresource: database + man: База данных создаётся на всех репликах кластера ClickHouse + params: + - id: 600 + code: databaseName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + descr: 'Пример: `db01`' + sort: 10 + - name: create_user + id: 185 + kind: subresource + action: create + subresource: user + params: + - id: 596 + code: username + data_type: string + required: true + regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ + maxlength: 62 + minlength: 2 + descr: 'Пример: `myusername`' + sort: 10 + - id: 598 + code: databaseName + data_type: json + required: true + default: '["database"]' + descr: Необходимо указать массив существующих баз + man: Если базы не существует - при выполнении операции вернётся ошибка + sort: 30 + - id: 599 + code: accessHosts + data_type: string + required: true + default: ::/0 + descr: 'Пример: `::/0`' + sort: 40 + - name: delete + id: 184 + kind: instance + action: delete + man: Удаление возможно только после остановки сервиса через операцию `suspend`
После выполнения операции необходимо выждать определенное кол-во времени + params: [] + - name: delete_database + id: 188 + kind: subresource + action: delete + subresource: database + man: Необратимое действие!!!
Трижды подумай, заблокируй телефон тимлида и не выполняй это действие + params: + - id: 602 + code: databaseName + data_type: string + required: true + descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ + - name: delete_user + id: 187 + kind: subresource + action: delete + subresource: user + params: + - id: 601 + code: username + required: true + descr: Имя пользователя, созданное через операцию `create_user` + - name: modify + id: 189 + kind: instance + action: modify + params: + - id: 603 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + man: '**Для синхронизации данных между репликами** необходимо включить `Создать ClickHouse-Keeper`' + sort: 10 + - id: 604 + code: resourceInstancesShards + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' + sort: 20 + - id: 605 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 8000 + minvalue: 1000 + descr: 'Пример: `1000`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 30 + - id: 606 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 1536 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 40 + - id: 607 + code: appVersion + data_type: string + required: true + default: "25.10" + descr: Версия `clickhouse-server` + sort: 50 + - id: 608 + code: resourceDisk + data_type: integer > 0 + required: true + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `100`' + man: Указывается в Gigabytes
**Нельзя** выставлять меньше текущего значения + sort: 60 + - id: 609 + code: needExternalAddressMaster + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Позволяет подключиться извне к ClickHouse + man: Для выделения IP также необходимо указать имя Ip-space.
Его можно получить из свойств ресурсной платформы + sort: 70 + - id: 610 + code: ipSpaceNameMaster + data_type: string + required: true + descr: 'Пример: `internet-no-antiddos-v1`' + man: Имя ipSpace для публикации внешнего адреса

Не может быть пустым, если включен `Выделение белого ip для master ноды` + sort: 80 + - id: 611 + code: needCreateKeeper + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Необходим для кластеризации ClickHouse + man: Аналог `Zookeeper`, представленный от ClickHouse + sort: 200 + - id: 612 + code: resourceCPUKeeper + data_type: integer > 0 + required: true + maxvalue: 8000 + minvalue: 50 + descr: 'Пример: `100`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 210 + - id: 613 + code: resourceMemoryKeeper + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 152 + descr: 'Пример: `256`' + man: Указывается в Megabytes + sort: 220 + - id: 614 + code: resourceInstancesKeeper + data_type: integer > 0 + required: true + value_list: + - "1" + - "3" + maxvalue: 3 + minvalue: 1 + descr: 'Пример: `1`' + man: Для **теста** - 1 нода.
Для **продуктива** - 3 ноды + sort: 230 + - id: 615 + code: ext_BACKUP_SCHEDULE + data_type: string + required: true + default: 0 0 * * * + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + descr: Принимает формат `CRON` + sort: 500 + - id: 616 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: true + maxvalue: 60 + minvalue: 3 + descr: 'Пример: `7`' + man: Кол-во **полных** резервных копий
Кол-во инкрементов - неограничено

Полная резервная копия выполняется раз в неделю
*Если изначально полной резервной копии нет* выполняется полный бекап
В остальные дни по крону выполняются **инкременты** + sort: 510 + - name: resume + id: 183 + kind: instance + action: resume + params: [] + - name: suspend + id: 182 + kind: instance + action: suspend + man: Операция приводит к удалению всех подов
Persistent Volume остаются нетронутыми + params: [] diff --git a/generated/dev/resources_yaml/12_s3.yaml b/generated/dev/resources_yaml/12_s3.yaml new file mode 100644 index 0000000..1bcb6be --- /dev/null +++ b/generated/dev/resources_yaml/12_s3.yaml @@ -0,0 +1,121 @@ +name: s3 +service_id: 12 +service_display_name: S3 Object Storage +service_short_name: s3 +service_man: Руководство доступно по ссылке:
https://docs.s3.msk-1.ngcloud.ru/s3/s3.description.html +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 1 + kind: instance + action: create + params: + - id: 163 + code: resourceRealm + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: кластер S3 для подключения + sort: 20 + - id: 320 + code: displayName + data_type: string + required: true + descr: Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента + sort: 10 + - name: create_sub_user + id: 6 + kind: subresource + action: create + subresource: sub_user + params: + - id: 60 + code: subUserName + data_type: string + required: true + - id: 61 + code: subUserAccess + data_type: string + required: true + value_list: + - read + - write + - readwrite + - full + - name: delete + id: 5 + kind: instance + action: delete + params: [] + - name: delete_sub_user + id: 7 + kind: subresource + action: delete + subresource: sub_user + params: + - id: 63 + code: subUserName + data_type: string + required: true + descr: subUserName. По идее услуга уже создана - должен передаваться из ЛК + - name: modify + id: 4 + kind: instance + action: modify + man: При первичном вызове операции услуга Trial прекращает действие + params: + - id: 50 + code: maxSizeGbPerUser + data_type: integer > 0 + required: false + maxvalue: 50000 + minvalue: 1 + descr: maxSizeGb. Передаём руками + man: maxSizeGb. Передаём руками + - id: 51 + code: maxObjectsPerBucket + data_type: integer > 0 + required: false + maxvalue: 1e+07 + minvalue: 1 + descr: Квота на бакет. Кол-во объектов. Передаём руками + man: Квота на бакет. Кол-во объектов. Передаём руками + - id: 52 + code: maxBucketsPerUser + data_type: integer > 0 + required: false + maxvalue: 1000 + minvalue: 1 + descr: Квота на пользователя. Количество бакетов пользователя. Передаём руками + man: Квота на пользователя. Количество бакетов пользователя. Передаём руками + - name: resume + id: 3 + kind: instance + action: resume + params: [] + - name: suspend + id: 2 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/13_s3bucket.yaml b/generated/dev/resources_yaml/13_s3bucket.yaml similarity index 100% rename from provider/resources_yaml/13_s3bucket.yaml rename to generated/dev/resources_yaml/13_s3bucket.yaml diff --git a/provider/resources_yaml/149_valo_tenant.yaml b/generated/dev/resources_yaml/149_valo_tenant.yaml similarity index 100% rename from provider/resources_yaml/149_valo_tenant.yaml rename to generated/dev/resources_yaml/149_valo_tenant.yaml diff --git a/generated/dev/resources_yaml/150_k8s_sthutrval_cluster.yaml b/generated/dev/resources_yaml/150_k8s_sthutrval_cluster.yaml new file mode 100644 index 0000000..4026df2 --- /dev/null +++ b/generated/dev/resources_yaml/150_k8s_sthutrval_cluster.yaml @@ -0,0 +1,114 @@ +name: k8s_sthutrval_cluster +service_id: 150 +service_display_name: Kubernetes кластер Штурвал +service_short_name: k8sSthutrvalCluster +service_man: Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 108 + kind: instance + action: create + params: + - id: 750 + code: startupConfiguration + data_type: map-fixed + required: true + sort: 10 + - id: 751 + code: controlPlaneConfiguration + data_type: map-fixed + required: true + sort: 30 + is_modifiable: true + - id: 752 + code: workerConfiguration + data_type: array-map-fixed + required: true + default: '[{}]' + sort: 40 + is_modifiable: true + - id: 753 + code: accessConfiguration + data_type: map-fixed + required: true + sort: 50 + is_modifiable: true + - id: 755 + code: clusterConfiguration + data_type: map-fixed + required: true + sort: 20 + is_modifiable: true + - name: delete + id: 109 + kind: instance + action: delete + params: [] + - name: modify + id: 110 + kind: instance + action: modify + man: При изменении кол-ва узлов, удаления группы, если в этой группе есть поды на которых есть PV с named-дисками, удаляться ноды сами НЕ БУДУТ.
Убедитесь, что они будут смигрированы на другие ноды в процессе прежде чем группа узлов/узлы будут удалены + params: + - id: 756 + code: clusterConfiguration + data_type: map-fixed + required: true + sort: 10 + - id: 757 + code: controlPlaneConfiguration + data_type: map-fixed + required: true + sort: 20 + - id: 758 + code: workerConfiguration + data_type: array-map-fixed + required: true + sort: 30 + - id: 759 + code: accessConfiguration + data_type: map-fixed + required: true + sort: 40 + - name: reconcile + id: 253 + kind: action + action: reconcile + params: [] + - name: resume + id: 174 + kind: instance + action: resume + params: [] + - name: suspend + id: 173 + kind: instance + action: suspend + params: [] + - name: update_secrets + id: 179 + kind: action + action: update_secrets + params: [] diff --git a/generated/dev/resources_yaml/151_k8s_openbao.yaml b/generated/dev/resources_yaml/151_k8s_openbao.yaml new file mode 100644 index 0000000..62c05ed --- /dev/null +++ b/generated/dev/resources_yaml/151_k8s_openbao.yaml @@ -0,0 +1,351 @@ +name: k8s_openbao +service_id: 151 +service_display_name: Vault +service_short_name: k8sOpenbao +service_man: '# Инструкция по развертыванию Хранилища секретов через платформу

## 1. Общая информация
Хранилище секретов — сервис централизованного и защищённого хранения чувствительных данных (секретов, ключей, конфигураций). Предназначен для использования приложениями и сервисами в Kubernetes с управлением доступом на основе ролей.
В качестве приложения используется открытый проект OpenBao с поддержкой кластерного режима RAFT.

### Доступные операции
**create** — Создание хранилища секретов и инициализация кластера.
**delete** — Удаление хранилища секретов с полной очисткой всех связанных сущностей.
**modify** — Изменение параметров развернутого хранилища секретов.
**suspend** — Приостановка работы кластера без удаления Persistent Volume.
**resume** — Возобновление работы кластера с автоматическим unseal всех нод.
**restart** — Поочередный перезапуск нод с последующим подключением в кластер.
**create_kv** — Создание Key/Value хранилища версии V2.
**delete_kv** — Удаление Key/Value хранилища со всеми содержащимися секретами.
**create_secrets** — Создание секрета внутри Key/Value хранилища.
**delete_secrets** — Удаление секрета из Key/Value хранилища.
**update_secrets** — Обновление значения секрета.
**recovery_secrets** — Откат секрета к указанной версии.
**create_role** — Создание роли для управления Key/Value хранилищем или секретами.
**update_role** — Обновление параметров роли.
**delete_role** — Удаление роли доступа.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: Кластер Kubernetes, в котором будет развернуто приложение.
Формат значения: имя Kubernetes-кластера.

### Имя домена
Описание: Доменное имя сервиса. При указании полного FQDN выполняется проверка DNS A-записи или добавляется А-запись в техническую зону.
Формат значения: secrets.domain.com / mysecretdomain.

### Кол-во реплик
Описание: Количество узлов в кластере.
Формат значения: целое число.

### Квота ядра пода
Описание: Лимит CPU для одного узла. Указывается в milicores (1000 milicores = 1 CPU).
Формат значения: целое число.

### Квота памяти пода
Описание: Лимит оперативной памяти для одного узла. Указывается в megabytes.
Формат значения: целое число.

### Размер диска под хранение секретов
Описание: Объем диска для хранения данных. Фактический объем равен значению, умноженному на количество реплик. Указывается в Gigabytes
Формат значения: целое число.

### Версия OpenBao
Описание: Версия OpenBao для развертывания сервиса.
Формат значения: выбор из предоставленного списка версий.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Кол-во реплик: 2.
Квота ядра пода: 200.
Квота памяти пода: 256.
Размер диска под хранение секретов: 1.
Версия OpenBao: актуальная стабильная версия.

### Промышленное окружение
Кол-во реплик: от 3.
Квота ядра пода: от 1000.
Квота памяти пода: от 1024.
Размер диска под хранение секретов: от 10.
Версия OpenBao: актуальная стабильная версия.

---

## 4. Выходные параметры
Настройки внутри хранилища (kv/secrets/roles) — конфигурации Key/Value, секретов и ролей, созданные соответствующими операциями.
Настройки самого хранилище (сайзинг, объемы) — параметры ресурсов и хранилища данных, примененные к сервису.
Статус кластера (running / suspended) — текущее состояние работы кластера.
url для подключения к веб — адрес веб-интерфейса сервиса.

---

## 5. Дополнительная информация
При удалении хранилища все данные и настройки безвозвратно удаляются.
При использовании доменного имени требуется корректная настройка DNS.
Операции управления секретами и ролями выполняются только после успешного создания хранилища.
Удаление кластера производится через операцию `suspend`, а удаление становится доступно через 14 дней после остановки кластера
Правило выше не относится к операции `delete_kv` / `delete_secrets` / `delete_role`' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 210 + kind: instance + action: create + params: + - id: 663 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Кластер Kubernetes в котором будет развёрнуто приложение + man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы + sort: 0 + - id: 664 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 8000 + minvalue: 250 + descr: 'Пример: `500`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 30 + is_modifiable: true + - id: 665 + code: resourceInstances + data_type: integer > 0 + required: true + default: "3" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + sort: 20 + is_modifiable: true + - id: 666 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 256 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 40 + is_modifiable: true + - id: 667 + code: resourceDisk + data_type: integer > 0 + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `10`' + man: Указывается в Gigabytes + sort: 50 + is_modifiable: true + - id: 668 + code: appVersion + data_type: string + required: true + default: 2.4.4 + value_list: + - 2.4.4 + descr: Выбрать из списка + sort: 60 + is_modifiable: true + - id: 669 + code: domain + data_type: string + required: true + default: bao- + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - name: create_kv + id: 212 + kind: subresource + action: create + subresource: kv + params: + - id: 675 + code: kvName + data_type: string + required: true + default: kvname- + regex: ^[a-zA-Z0-9-]{1,24}$ + man: Будет создан `v2 тип движка`

Уникальное в рамках услуги + sort: 10 + - name: create_policy + id: 237 + kind: subresource + action: create + subresource: policy + params: + - id: 728 + code: policySettings + data_type: array-map-fixed + required: true + sort: 20 + - id: 729 + code: policyName + required: true + default: policy- + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + man: Сделано для дальнейшего удобного использования + sort: 10 + - name: create_secrets + id: 214 + kind: subresource + action: create + subresource: secrets + params: + - id: 677 + code: kvName + data_type: string + required: true + descr: Выберите из списка + man: Если список пустой, создайте kv через операцию `create_kv` + sort: 10 + - id: 678 + code: secretPath + data_type: string + required: true + default: path/to/mysecret + regex: ^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$ + descr: 'Пример: `path` или `road/to/path`' + sort: 20 + - id: 679 + code: secretData + data_type: map + required: true + descr: Хотя бы один секрет необходимо добавить + man: Данные не сохраняются в ЛК, в дальнейшем при обновлении секретов это необходимо учитывать + sort: 30 + - name: delete + id: 231 + kind: instance + action: delete + params: [] + - name: delete_kv + id: 213 + kind: subresource + action: delete + subresource: kv + man: '**ОСТОРОЖНО**
Удаление KV подразумевает удаление ВСЕХ secrets внутри этой KV' + params: + - id: 676 + code: kvName + data_type: string + required: true + descr: Выберите из списка + sort: 10 + - name: delete_policy + id: 239 + kind: subresource + action: delete + subresource: policy + params: + - id: 732 + code: policyName + data_type: string + required: true + - name: delete_secrets + id: 233 + kind: subresource + action: delete + subresource: secrets + params: + - id: 726 + code: kvName + data_type: string + required: true + descr: Выберите из списка + sort: 10 + - id: 727 + code: secretPath + data_type: string + required: true + descr: Выберите из списка + sort: 20 + - name: modify + id: 211 + kind: instance + action: modify + params: + - id: 670 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + sort: 10 + - id: 671 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 8000 + minvalue: 250 + descr: 'Пример: `500`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 20 + - id: 672 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 256 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 30 + - id: 673 + code: resourceDisk + data_type: integer > 0 + required: true + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `10`' + man: Указывается в Gigabytes + sort: 40 + - id: 674 + code: appVersion + data_type: string + required: true + value_list: + - 2.4.4 + descr: Выбрать из списка + sort: 50 + - name: reconcile + id: 236 + kind: action + action: reconcile + params: [] + - name: recovery_secrets + id: 232 + kind: action + action: recovery_secrets + man: Данная операция подразумевает откат на конкретную версию в секрете.

**Это не восстановление удалённого секрета** + params: + - id: 723 + code: kvName + data_type: string + required: true + descr: Выберите из списка + sort: 10 + - id: 724 + code: secretPath + data_type: string + required: true + descr: Выберите из списка + sort: 20 + - id: 725 + code: secretVersion + data_type: string + required: true + - name: restart + id: 234 + kind: action + action: restart + params: [] + - name: resume + id: 216 + kind: instance + action: resume + params: [] + - name: suspend + id: 215 + kind: instance + action: suspend + params: [] + - name: update_policy + id: 238 + kind: action + action: update_policy + params: + - id: 730 + code: policyName + data_type: string + required: true + sort: 10 + - id: 731 + code: policySettings + data_type: array-map-fixed + required: true + descr: Производится полная перезапись политики! + sort: 20 + - name: update_secrets + id: 230 + kind: action + action: update_secrets + params: + - id: 719 + code: kvName + data_type: string + required: true + descr: Выберите из списка + sort: 10 + - id: 720 + code: secretPath + data_type: string + required: true + descr: Выберите из списка + man: Предварительно необходимо выбрать параметр `Имя Key/Value хранилища` + sort: 20 + - id: 721 + code: needFullUpdateSecret + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + man: Если выбрать `true`, будет создана новая версия секрета `с нуля`
Если выбрать `false`, будет создана новая версия секрета с данными прошлой ревизии, а переданные данные в операции либо будут модифицированы, либо будут добавлены + sort: 30 + - id: 722 + code: secretData + data_type: map + required: true + descr: Хотя бы один секрет необходимо добавить + sort: 40 diff --git a/generated/dev/resources_yaml/153_nifi.yaml b/generated/dev/resources_yaml/153_nifi.yaml new file mode 100644 index 0000000..a352f88 --- /dev/null +++ b/generated/dev/resources_yaml/153_nifi.yaml @@ -0,0 +1,109 @@ +name: nifi +service_id: 153 +service_display_name: Nifi +service_short_name: nifi +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 229 + kind: instance + action: create + params: + - id: 711 + code: domain + data_type: string + required: true + default: nifi- + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 712 + code: resourceCPU + data_type: integer > 0 + required: true + default: "2000" + maxvalue: 8000 + minvalue: 2000 + descr: 'Пример: `2000`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 30 + - id: 713 + code: resourceMemory + data_type: integer > 0 + required: true + default: "4096" + maxvalue: 16384 + minvalue: 2048 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 40 + - id: 714 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + value_list: + - "1" + maxvalue: 1 + minvalue: 1 + descr: 'Пример: `1`' + sort: 20 + - id: 715 + code: resourceDisk + data_type: integer > 0 + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `10`' + man: Указывается в Gigabytes + sort: 50 + - id: 716 + code: appVersion + data_type: string + required: true + default: 2.6.0 + value_list: + - 2.6.0 + descr: Выбрать из списка + sort: 60 + - id: 717 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Кластер Kubernetes в котором будет развёрнуто приложение + man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы + sort: 0 + - id: 718 + code: jdbcDrivers + data_type: array-map-fixed + required: true + man: 'Пример: `https://jdbc.postgresql.org/download/postgresql-42.7.8.jar`' + - name: delete + id: 235 + kind: instance + action: delete + params: [] diff --git a/generated/dev/resources_yaml/19_vc_org.yaml b/generated/dev/resources_yaml/19_vc_org.yaml new file mode 100644 index 0000000..666db83 --- /dev/null +++ b/generated/dev/resources_yaml/19_vc_org.yaml @@ -0,0 +1,86 @@ +name: vc_org +service_id: 19 +service_display_name: Организация в Cloud Director +service_short_name: vcOrg +service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 136 + kind: instance + action: create + man: '`IaaS` - Доступ в Keycloak предоставляется пользователю, который создаёт услугу
`SaaS` - Без доступа к Keycloak' + params: + - id: 418 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Облако в котором будет развёрнут тенант + man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы + sort: 10 + - id: 556 + code: organizationType + data_type: string + required: true + default: iaas + value_list: + - iaas + - saas + man: Доступ к организации предоставляется только при выборе `iaas` + sort: 20 + - name: delete + id: 137 + kind: instance + action: delete + man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта**

Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' + params: [] + - name: modify + id: 207 + kind: instance + action: modify + man: Для возможности изменения параметров предварительно необходимо создать **Виртуальный датацентр (VDC)**, потом **Сетевой шлюз периметра (Edge Gateway)**
Также при удалении ipSpaces убедитесь, что все внешние IP удалены. В случае если хоть одна привязка vIP есть, данный ipSpace удалён **не будет** + params: + - id: 662 + code: vIPConfigure + data_type: array-map-fixed + required: true + default: '[{}]' + sort: 10 + - name: reconcile + id: 249 + kind: action + action: reconcile + params: [] + - name: resume + id: 144 + kind: instance + action: resume + params: [] + - name: suspend + id: 143 + kind: instance + action: suspend + man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта + params: [] diff --git a/generated/dev/resources_yaml/1_dummy.yaml b/generated/dev/resources_yaml/1_dummy.yaml new file mode 100644 index 0000000..30305c9 --- /dev/null +++ b/generated/dev/resources_yaml/1_dummy.yaml @@ -0,0 +1,208 @@ +name: dummy +service_id: 1 +service_display_name: Болванка +service_short_name: dummy +service_man: '## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
```' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 18 + kind: instance + action: create + man: Это мануал к операции CREATE сервиса Болванка + params: + - id: 198 + code: durationMs + data_type: integer >= 0 + required: true + default: "0" + descr: перед завершением подождать (sleep) столько миллисекунд + man: перед завершением подождать (sleep) столько миллисекунд + sort: 20 + is_modifiable: true + - id: 199 + code: failAtStart + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, завершить джоб, ничего не записывая в cmdb + man: '**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой' + sort: 30 + is_modifiable: true + - id: 200 + code: failInProgress + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + man: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + sort: 40 + is_modifiable: true + - id: 201 + code: whereFail + data_type: integer > 0 + required: false + default: "1" + value_list: + - "1" + - "2" + - "3" + descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + sort: 50 + is_modifiable: true + - id: 242 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование ресурсной платформы для подключения + sort: 10 + - id: 286 + code: bodymessage + data_type: string + required: false + descr: Дата, уходящая в Vault + man: Дата, уходящая в Vault + sort: 60 + is_modifiable: true + - id: 321 + code: mapExample + data_type: map + required: false + is_modifiable: true + - id: 322 + code: jsonExample + data_type: json + required: false + descr: Для тестирования интерфейса + man: Для тестирования интерфейса. Сюда можно писать корректный json + is_modifiable: true + - id: 396 + code: nestedRefExample + required: false + - id: 447 + code: yamlExample + data_type: yaml + required: false + - id: 647 + code: mapFixed + data_type: map-fixed + required: false + descr: Новый тип поля + - id: 654 + code: arrayMapFixedExample + data_type: array-map-fixed + required: false + descr: Array of map-fixed + - name: delete + id: 71 + kind: instance + action: delete + params: [] + - name: modify + id: 92 + kind: instance + action: modify + params: + - id: 287 + code: durationMs + data_type: integer >= 0 + required: true + default: "0" + descr: перед завершением подождать (sleep) столько миллисекунд + man: перед завершением подождать (sleep) столько миллисекунд + sort: 10 + - id: 288 + code: failAtStart + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, завершить джоб, ничего не записывая в cmdb + sort: 20 + - id: 289 + code: failInProgress + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + sort: 30 + - id: 290 + code: whereFail + data_type: integer > 0 + required: false + value_list: + - "1" + - "2" + - "3" + descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + sort: 40 + - id: 291 + code: bodymessage + required: false + descr: Дата, уходящая в Vault + man: Дата, уходящая в Vault + sort: 50 + - id: 373 + code: mapExample + data_type: map + required: false + default: '{}' + - id: 374 + code: jsonExample + required: false + - name: reconcile + id: 257 + kind: action + action: reconcile + params: [] + - name: redeploy + id: 240 + kind: action + action: redeploy + params: [] + - name: resume + id: 94 + kind: instance + action: resume + params: [] + - name: suspend + id: 93 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/21_vc_vdc.yaml b/generated/dev/resources_yaml/21_vc_vdc.yaml new file mode 100644 index 0000000..4870b3a --- /dev/null +++ b/generated/dev/resources_yaml/21_vc_vdc.yaml @@ -0,0 +1,142 @@ +name: vc_vdc +service_id: 21 +service_display_name: Виртуальный датацентр (vDC) +service_short_name: vc_vdc +service_man: '# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 9 + kind: instance + action: create + params: + - id: 30 + code: organizationUid + data_type: uuid + required: true + ref_svc_id: 19 + man: Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` + sort: 10 + - id: 335 + code: providerVdc + data_type: string + required: true + man: Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
+ sort: 30 + depends_on: organizationUid,networkProvider + - id: 361 + code: storageConfig + data_type: array-map-fixed + required: true + default: '[{}]' + man: Первая политика в списке указывается как "По умолчанию" + sort: 100 + is_modifiable: true + - id: 397 + code: cpuGuaranteed + data_type: integer > 0 + required: true + default: "20" + value_list: + - "20" + - "50" + - "80" + descr: '% резервирования' + man: Свойство нельзя изменить после создания + sort: 60 + - id: 557 + code: cpuAllocated + data_type: integer > 0 + required: true + default: "10" + maxvalue: 200 + minvalue: 1 + descr: шт + sort: 80 + is_modifiable: true + - id: 558 + code: memAllocated + data_type: integer > 0 + required: true + default: "20" + maxvalue: 4096 + minvalue: 1 + descr: gb + sort: 90 + is_modifiable: true + - id: 746 + code: networkProvider + data_type: string + required: true + sort: 20 + depends_on: organizationUid + - name: delete + id: 24 + kind: instance + action: delete + man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC**

Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' + params: [] + - name: modify + id: 112 + kind: instance + action: modify + params: + - id: 560 + code: cpuAllocated + data_type: integer > 0 + required: true + maxvalue: 999 + minvalue: 1 + descr: шт + sort: 10 + - id: 561 + code: memAllocated + data_type: integer > 0 + required: true + maxvalue: 1024 + minvalue: 1 + descr: gb + sort: 20 + - id: 562 + code: storageConfig + data_type: array-map-fixed + required: true + man: Первая политика в списке указывается как "По умолчанию" + sort: 30 + - name: reconcile + id: 250 + kind: action + action: reconcile + params: [] + - name: resume + id: 172 + kind: instance + action: resume + params: [] + - name: suspend + id: 171 + kind: instance + action: suspend + man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC + params: [] diff --git a/generated/dev/resources_yaml/22_vc_nsxt.yaml b/generated/dev/resources_yaml/22_vc_nsxt.yaml new file mode 100644 index 0000000..4c2aaa3 --- /dev/null +++ b/generated/dev/resources_yaml/22_vc_nsxt.yaml @@ -0,0 +1,136 @@ +name: vc_nsxt +service_id: 22 +service_display_name: Сетевой шлюз периметра (Edge) +service_short_name: vc_nsxt +service_man: '# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 10 + kind: instance + action: create + params: + - id: 8 + code: vdcUid + data_type: string + required: false + ref_svc_id: 21 + descr: UUID Услуги `Виртуальный датацентр (vDC)` + man: 'Необходимо, если выбран тип подключаемой VDC: `vdc`' + sort: 10 + - id: 340 + code: needEnableAVI + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включение Load Balancer + man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать + sort: 30 + is_modifiable: true + - id: 341 + code: virtualServicesCount + data_type: integer > 0 + required: false + default: "1" + maxvalue: 4 + minvalue: 1 + descr: Кол-во виртуальных сервисов, которые **резервируются** на AVI + man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` + sort: 50 + is_modifiable: true + - id: 621 + code: vdcType + data_type: string + required: true + default: vdc + value_list: + - vdc + - vdcGroup + descr: Тип родительской услуги + man: При выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc + sort: 0 + - id: 622 + code: vdcGroupUid + data_type: string + required: false + ref_svc_id: 29 + descr: UUID Услуги `Группа виртуальных датацентров (groupvDC)` + man: 'Необходимо, если выбран тип подключаемой VDC: `groupvdc`' + sort: 20 + - name: delete + id: 25 + kind: instance + action: delete + man: Невозможно удалить если routed-сеть в Edge связана с:
- vApp
- VM
- Kubernetes-кластер Штурвал

Запустить операцию возможно, но будет ошибка + params: [] + - name: modify + id: 111 + kind: instance + action: modify + man: Для создания SNAT Правила убедитесь, что в организации есть свободные IP
Редактировать кол-во свободных IP можно в услуге `Организация в Cloud Director` -> `modify` + params: + - id: 368 + code: needEnableAVI + data_type: boolean + required: false + value_list: + - "false" + - "true" + descr: Включение Load Balancer + man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать + sort: 10 + - id: 369 + code: virtualServicesCount + data_type: integer > 0 + required: false + maxvalue: 4 + minvalue: 1 + descr: Кол-во виртуальных сервисов, которые выделяются на AVI + man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` + sort: 30 + - id: 371 + code: needExternalAddressSNAT + data_type: boolean + required: false + value_list: + - "false" + - "true" + descr: Создать правило SNAT на подсеть `10.10.102.0/24` + man: Параметр `ip Space для VIP` станет обязательным, если указан `true` + sort: 40 + - id: 372 + code: ipSpaceName + data_type: string + required: false + descr: Имя ip Space для внешнего IP + man: Необходимо указывать, если включён параметр `Выделить VIP для SNAT` + sort: 50 + - name: reconcile + id: 252 + kind: action + action: reconcile + params: [] diff --git a/provider/resources_yaml/25_vcexternalip.yaml b/generated/dev/resources_yaml/25_vcexternalip.yaml similarity index 100% rename from provider/resources_yaml/25_vcexternalip.yaml rename to generated/dev/resources_yaml/25_vcexternalip.yaml diff --git a/generated/dev/resources_yaml/26_vapp.yaml b/generated/dev/resources_yaml/26_vapp.yaml new file mode 100644 index 0000000..b48cabd --- /dev/null +++ b/generated/dev/resources_yaml/26_vapp.yaml @@ -0,0 +1,73 @@ +name: vapp +service_id: 26 +service_display_name: Виртуальный каталог ВМ (vApp) +service_short_name: vapp +service_man: '# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 69 + kind: instance + action: create + params: + - id: 190 + code: nsxtUid + data_type: uuid + required: true + ref_svc_id: 22 + descr: UUID Услуги `Сетевой шлюз периметра (Edge)` + man: К какой сети может быть подключен виртуальный каталог + sort: 20 + - id: 191 + code: vappName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$ + minlength: 3 + descr: Уникальное в рамках организации + man: Имеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин) + sort: 30 + - id: 623 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + descr: UUID Услуги `Виртуальный датацентр (vDC)` + man: В каких ресурсах будет создан виртуальный каталог + sort: 10 + - name: delete + id: 70 + kind: instance + action: delete + params: [] + - name: resume + id: 209 + kind: instance + action: resume + params: [] + - name: suspend + id: 208 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/28_vc_vm_v3.yaml b/generated/dev/resources_yaml/28_vc_vm_v3.yaml new file mode 100644 index 0000000..e800f5a --- /dev/null +++ b/generated/dev/resources_yaml/28_vc_vm_v3.yaml @@ -0,0 +1,273 @@ +name: vc_vm_v3 +service_id: 28 +service_display_name: Виртуальная машина +service_short_name: vc_vm_v3 +service_man: '# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 132 + kind: instance + action: create + man: '# Документация по использованию услуги развёртывания ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\лож определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed".

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' + params: + - id: 407 + code: vappUid + data_type: uuid + required: true + ref_svc_id: 26 + sort: 10 + - id: 408 + code: vmName + data_type: string + required: true + default: web01 + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + sort: 20 + - id: 409 + code: vmCpu + data_type: integer > 0 + required: true + default: "1" + maxvalue: 64 + minvalue: 1 + descr: шт + sort: 30 + is_modifiable: true + - id: 410 + code: vmRam + data_type: integer > 0 + required: true + default: "1" + maxvalue: 256 + minvalue: 1 + descr: Gb + sort: 40 + is_modifiable: true + - id: 411 + code: vmDisk + data_type: integer > 0 + required: false + regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ + descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму + sort: 50 + is_modifiable: true + - id: 412 + code: ipSpaceName + data_type: string + required: true + sort: 60 + depends_on: vappUid + is_modifiable: true + - id: 413 + code: accessIpList + data_type: json + required: false + regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ + descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] + sort: 70 + is_modifiable: true + - id: 414 + code: imageVm + data_type: string + required: true + sort: 90 + depends_on: vappUid + - id: 415 + code: cloudInit + data_type: yaml + required: false + descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. + sort: 200 + - id: 416 + code: userLogin + required: true + default: myuser + descr: Учётка SSH + sort: 180 + - id: 417 + code: userPublicKey + data_type: string + required: true + descr: Публичная часть SSH-Ключа для пользователя + sort: 190 + - id: 448 + code: accessPortList + data_type: string + required: true + default: '["22"]' + descr: 'По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX' + sort: 80 + is_modifiable: true + - name: delete + id: 133 + kind: instance + action: delete + params: [] + - name: modify + id: 159 + kind: instance + action: modify + params: + - id: 493 + code: vmCpu + data_type: integer > 0 + required: false + maxvalue: 64 + minvalue: 1 + descr: шт + sort: 20 + - id: 494 + code: vmRam + data_type: integer > 0 + required: false + maxvalue: 256 + minvalue: 1 + descr: Gb + sort: 30 + - id: 495 + code: vmDisk + data_type: integer > 0 + required: false + regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ + descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму + sort: 40 + - id: 496 + code: ipSpaceName + data_type: string + required: false + value_list: + - no-needed + - internet-antiddos-v1 + - internet-no-antiddos-v1 + - internet-ipv4-v1 + descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev + sort: 50 + - id: 497 + code: accessIpList + data_type: json + required: false + descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] + sort: 60 + - id: 498 + code: accessPortList + data_type: json + required: false + descr: Формат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX + sort: 70 + - name: redeploy + id: 164 + kind: action + action: redeploy + params: + - id: 509 + code: vmName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + sort: 10 + - id: 510 + code: vmCpu + data_type: integer > 0 + required: true + maxvalue: 64 + minvalue: 1 + descr: шт + sort: 20 + - id: 511 + code: vmRam + data_type: integer > 0 + required: true + maxvalue: 256 + minvalue: 1 + descr: Gb + sort: 30 + - id: 512 + code: vmDisk + data_type: integer > 0 + required: false + regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ + descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму + sort: 40 + - id: 513 + code: ipSpaceName + data_type: string + required: false + value_list: + - no-needed + - internet-antiddos-v1 + - internet-no-antiddos-v1 + - internet-ipv4-v1 + descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev + sort: 50 + - id: 514 + code: accessIpList + data_type: json + required: false + regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ + descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] + sort: 60 + - id: 515 + code: imageVm + data_type: string + required: true + value_list: + - RockyLinux_9-16G-cloudinit + - Ubuntu_22-20G + - Debian_13-20G + sort: 80 + - id: 516 + code: userLogin + data_type: string + required: true + descr: Учётка SSH + sort: 180 + - id: 517 + code: userPublicKey + data_type: string + required: true + descr: Публичная часть SSH-Ключа для пользователя + sort: 190 + - id: 518 + code: cloudInit + data_type: yaml + required: false + descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. + sort: 200 + - id: 520 + code: accessPortList + data_type: json + required: false + descr: По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX + sort: 70 + - name: resume + id: 135 + kind: instance + action: resume + params: [] + - name: suspend + id: 134 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/29_vc_vdc_group.yaml b/generated/dev/resources_yaml/29_vc_vdc_group.yaml new file mode 100644 index 0000000..efec3aa --- /dev/null +++ b/generated/dev/resources_yaml/29_vc_vdc_group.yaml @@ -0,0 +1,72 @@ +name: vc_vdc_group +service_id: 29 +service_display_name: Группа датацентров +service_short_name: vcVdcGroup +service_man: '# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc`' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: add_vdc + id: 192 + kind: action + action: add_vdc + man: '**Не нужно** выбирать UUID VDC, который уже был добавлен в группу' + params: + - id: 619 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + sort: 10 + - name: create + id: 191 + kind: instance + action: create + man: '**VDC Группа** нужна для объединения всех VDC в организации в одну логическую группу.
Это позволит, к примеру, создать **общую сеть** для всех VDC в организации

- Для создания группы VDC необходимо указать **инстанс** услуги VDC (`Виртуальный датацентр`)
- **Важный момент** - удалить эту VDC из группы нельзя. Если такая потребность появится, необходимо будет **ПОЛНОСТЬЮ** удалить группу VDC и уже потом создать новую' + params: + - id: 618 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + sort: 10 + - name: delete + id: 195 + kind: instance + action: delete + params: [] + - name: reconcile + id: 251 + kind: action + action: reconcile + params: [] + - name: remove_vdc + id: 193 + kind: action + action: remove_vdc + params: + - id: 620 + code: vdcName + data_type: string + required: true + sort: 10 diff --git a/generated/dev/resources_yaml/2_template.yaml b/generated/dev/resources_yaml/2_template.yaml new file mode 100644 index 0000000..d37f0bd --- /dev/null +++ b/generated/dev/resources_yaml/2_template.yaml @@ -0,0 +1,128 @@ +name: template +service_id: 2 +service_display_name: Темплейт k8s +service_short_name: template +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 74 + kind: instance + action: create + params: + - id: 202 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 100 + is_modifiable: true + - id: 203 + code: resourceMemory + data_type: integer > 0 + required: true + default: "200" + maxvalue: 8192 + minvalue: 128 + is_modifiable: true + - id: 204 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + is_modifiable: true + - id: 205 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + is_modifiable: true + - id: 206 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - id: 207 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + - name: delete + id: 75 + kind: instance + action: delete + params: [] + - name: modify + id: 76 + kind: instance + action: modify + params: + - id: 208 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + - id: 209 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + - id: 210 + code: resourceDisk + data_type: integer > 0 + required: false + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + - id: 211 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + - name: resume + id: 98 + kind: instance + action: resume + params: [] + - name: suspend + id: 97 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/50_nextcloud.yaml b/generated/dev/resources_yaml/50_nextcloud.yaml new file mode 100644 index 0000000..b64448e --- /dev/null +++ b/generated/dev/resources_yaml/50_nextcloud.yaml @@ -0,0 +1,184 @@ +name: nextcloud +service_id: 50 +service_display_name: Nextcloud +service_short_name: nextcloud +service_man: 'Ресурсная платформа: k8s. Автоматизация: helm chart' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 16 + kind: instance + action: create + man: DEV-Ветка сборки Nextcloud + params: + - id: 21 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Наименование домена. Если будет указан FQDN, то происходят проверки DNS. Если указан поддомен, то будет добавлен в зону services.ngcloud.ru
Можно указать клиентскую DNS-Запись. Важно, чтобы запись смотрела на Ingress кластера + sort: 10 + - id: 99 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili + man: Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili + sort: 30 + is_modifiable: true + - id: 100 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 1024 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 40 + is_modifiable: true + - id: 101 + code: resourceDisk + data_type: integer > 0 + required: true + default: "2" + maxvalue: 999 + minvalue: 2 + descr: Квота диска (Указывается в GIGAbytes)
Диск не может быть меньше 2G (!) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 192 + code: documentServer + data_type: string + required: true + default: collabora + value_list: + - collabora + descr: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ + man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен + sort: 100 + - id: 470 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + value_list: + - "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов
Не может пока быть больше одного, так как нужны SC под RWMany + sort: 60 + is_modifiable: true + - id: 564 + code: psqlUid + data_type: uuid + required: true + ref_svc_id: 90 + sort: 6 + - id: 569 + code: appVersion + required: true + default: 32.0.1 + value_list: + - 32.0.1 + sort: 20 + is_modifiable: true + - id: 570 + code: redisUid + data_type: uuid + required: true + ref_svc_id: 91 + sort: 7 + - name: delete + id: 17 + kind: instance + action: delete + man: DEV-Ветка удаления Nextcloud + params: [] + - name: modify + id: 67 + kind: instance + action: modify + params: + - id: 178 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) + man: Квота ядра пода (Указывается в mili) + - id: 179 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + - id: 180 + code: resourceDisk + data_type: integer > 0 + required: false + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + - id: 547 + code: resourceInstances + data_type: integer > 0 + required: false + value_list: + - "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов.
Не может пока быть больше одного, так как нужны SC под RWMany + - id: 571 + code: appVersion + data_type: string + required: true + value_list: + - 32.0.1 + descr: Нельзя обновляться сразу через несколько версий сразу + - name: restart + id: 66 + kind: action + action: restart + params: [] + - name: resume + id: 129 + kind: instance + action: resume + params: [] + - name: suspend + id: 128 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/81_superset.yaml b/generated/dev/resources_yaml/81_superset.yaml similarity index 100% rename from provider/resources_yaml/81_superset.yaml rename to generated/dev/resources_yaml/81_superset.yaml diff --git a/generated/dev/resources_yaml/82_harbor.yaml b/generated/dev/resources_yaml/82_harbor.yaml new file mode 100644 index 0000000..c47b0df --- /dev/null +++ b/generated/dev/resources_yaml/82_harbor.yaml @@ -0,0 +1,103 @@ +name: harbor +service_id: 82 +service_display_name: Container Registry +service_short_name: harbor +service_man: 'Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 14 + kind: instance + action: create + man: DEV-Ветка сборки Harbor + params: + - id: 16 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 20 + - id: 17 + code: emails + data_type: string + required: true + default: testuser1 + descr: Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел + man: Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел + sort: 70 + - id: 110 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 30 + - id: 228 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 40 + - id: 229 + code: resourceMemory + data_type: integer > 0 + required: true + default: "200" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 50 + - id: 230 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 60 + - id: 231 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: UUID экземпляра услуги S3 + man: UUID экземпляра услуги S3 + sort: 10 + - name: delete + id: 15 + kind: instance + action: delete + man: DEV-Ветка удаления Harbor + params: [] diff --git a/provider/resources_yaml/88_k8s_ziti_controller.yaml b/generated/dev/resources_yaml/88_k8s_ziti_controller.yaml similarity index 100% rename from provider/resources_yaml/88_k8s_ziti_controller.yaml rename to generated/dev/resources_yaml/88_k8s_ziti_controller.yaml diff --git a/generated/dev/resources_yaml/89_flask.yaml b/generated/dev/resources_yaml/89_flask.yaml new file mode 100644 index 0000000..437d94d --- /dev/null +++ b/generated/dev/resources_yaml/89_flask.yaml @@ -0,0 +1,165 @@ +name: flask +service_id: 89 +service_display_name: Web-сервер с фреймворком Flask +service_short_name: flask +service_man: '# Инструкция по развертыванию Web-сервер с фреймворком Flask через платформу

## 1. Общая информация

Сервис **Web-сервер с фреймворком Flask** предназначен для развертывания веб-приложений на базе Flask.

### Доступные операции

create — включить сервис, развернуть веб-приложение с указанными параметрами.
delete — удалить сервис, только после suspend.
modify — изменить настройки сервиса и переразвернуть.
suspend — выключить веб-приложение, остановить обслуживание.
resume — включить веб-приложение после suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Имя домена
Описание: доменное имя для доступа к веб-приложению. Если указан FQDN, происходит проверка DNS на валидность А-записи. А-запись должна указывать на балансировщик ресурсной платформы. Если указано имя в рамках одного октета, оно будет добавлено в публичную DNS-зону.
Пример значения: строка, допустимые FQDN или имя в одном октете. Пример: example.com или myapp.

### Платформа для развертывания
Описание: кластер Kubernetes, в котором будет развернуто приложение.
Пример значения: идентификатор кластера Kubernetes.

### Квота ядра пода
Описание: количество CPU, выделяемое на под, в Milicores.
Пример значения: целое число, например 500 для 0.5 ядра.

### Квота памяти пода
Описание: количество памяти, выделяемое на под, в Megabytes.
Пример значения: целое число, например 512 для 512 MB.

### Кол-во реплик пода
Описание: количество инстансов пода для масштабирования.
Пример значения: целое число, шт, например 2.

### URL Проекта (gitpath)
Описание: полный HTTPS путь до проекта в Git-репозитории.
Пример значения: URL в формате https://github.com/myuser/myproject.git. Доступ к репозиторию должен быть публичным.

### URL HealthCheck
Описание: путь для проверки здоровья приложения внутри пода.
Пример значения: строка, путь, например /api/v1/health.

### Переменные окружения пода
Описание: структура данных, которая будет передана как переменные окружения в под.
Пример значения: ключ-значение, например {"ENV": "production"}.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Квота ядра пода: 300m (0.3 ядра)
- Квота памяти пода: 256 MB
- Кол-во реплик пода: 1

### Промышленное окружение
- Квота ядра пода: от 1000m (1 ядро) в зависимости от нагрузки
- Квота памяти пода: от 1024 MB (1 GB) с учетом потребностей приложения
- Кол-во реплик пода: от 2 для обеспечения отказоустойчивости

---

## 4. Выходные параметры
Операция create не возвращает явных выходных параметров. Успешное выполнение операции приводит к развертыванию сервиса, доступного по указанному домену.

---

## 5. Дополнительная информация
- В корне проекта должен находиться файл requirements.txt со списком зависимостей Python.
- Исходный код веб-приложения должен располагаться в директории site.
- Запуск веб-сервера осуществляется командой python app.py.
- Используется версия Python: python:3.12-slim.
- Git-репозиторий должен быть публично доступен для платформы.
- Операция delete возможна только после suspend.
- Операция modify приводит к переразвертыванию с новыми параметрами.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 88 + kind: instance + action: create + params: + - id: 248 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + man: Если будет указан `FQDN`, то происходят проверки DNS на валидность А-записи. А-запись **обязана смотреть на балансировщик** ресурсной платформы.
Если указано имя `в рамках одного октета`, будет добавлено **в публичную днс-зону** + sort: 10 + - id: 249 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Кластер Kubernetes в котором будет развёрнуто приложение + man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы + sort: 20 + - id: 250 + code: resourceCPU + data_type: integer > 0 + required: true + default: "300" + maxvalue: 8000 + minvalue: 100 + descr: 'Пример: `1000`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 30 + is_modifiable: true + - id: 251 + code: resourceMemory + data_type: integer > 0 + required: true + default: "256" + maxvalue: 8192 + minvalue: 128 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 40 + is_modifiable: true + - id: 252 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + man: Балансировка производится между всеми репликами + sort: 50 + is_modifiable: true + - id: 253 + code: gitPath + data_type: string + required: true + default: https://github.com/Foxyhhd/Baldurs-Gate-test.git + descr: 'Пример: `https://github.com/myuser/myproject.git`' + man: Проект должен быть публичным + sort: 60 + is_modifiable: true + - id: 254 + code: jsonEnv + data_type: map + required: false + man: Структура, которая будет передана как переменные окружения + sort: 80 + is_modifiable: true + - id: 255 + code: healthPath + data_type: string + required: false + descr: 'Пример: `/api/v1/health`' + man: Может быть пустым. Если ничего не передано, проверяется `tcpsocket`
Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код + sort: 70 + - name: delete + id: 89 + kind: instance + action: delete + man: Удаление возможно только после остановки сервиса через операцию `suspend`
После выполнения операции необходимо выждать определенное кол-во времени + params: [] + - name: modify + id: 146 + kind: instance + action: modify + params: + - id: 451 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 8000 + minvalue: 100 + descr: 'Пример: `1000`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 10 + - id: 452 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 128 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 20 + - id: 453 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + man: Балансировка производится между всеми репликами + sort: 30 + - id: 454 + code: gitPath + required: true + descr: 'Пример: `https://github.com/myuser/myproject.git`' + man: Проект должен быть публичным + sort: 40 + - id: 455 + code: jsonEnv + data_type: map + required: true + man: Структура, которая будет передана как переменные окружения + sort: 50 + - name: resume + id: 145 + kind: instance + action: resume + params: [] + - name: suspend + id: 125 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/90_postgres.yaml b/generated/dev/resources_yaml/90_postgres.yaml new file mode 100644 index 0000000..7811991 --- /dev/null +++ b/generated/dev/resources_yaml/90_postgres.yaml @@ -0,0 +1,519 @@ +name: postgres +service_id: 90 +service_display_name: PostgreSQL +service_short_name: postgres +service_man: '# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 19 + kind: instance + action: create + man: руководство + params: + - id: 23 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + man: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + sort: 20 + - id: 80 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + regex: ^[1357]$ + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в кластере. 1,3,5,7 + sort: 60 + is_modifiable: true + - id: 81 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 82 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + man: Квота ядра пода (milicores) + sort: 30 + is_modifiable: true + - id: 83 + code: resourceDisk + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 102 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Кластер Kubernetes, на котором будет развернут экземпляр + sort: 10 + - id: 145 + code: needExternalAddressMaster + data_type: boolean + required: false + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к master + man: Выделяет внешний (белый) IP для доступа к master + sort: 70 + is_modifiable: true + - id: 146 + code: needExternalAddressSlave + data_type: boolean + required: false + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть + man: Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть + sort: 90 + is_modifiable: true + - id: 265 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + default: 0 0 * * * + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + man: Не обязательный. Время запуска бекапа + sort: 150 + is_modifiable: true + - id: 266 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: false + default: "7" + maxvalue: 99 + minvalue: 1 + man: Не обязательный. Количество сохраняемых бекапов + sort: 140 + is_modifiable: true + - id: 310 + code: appVersion + data_type: string + required: true + default: "17" + value_list: + - "17" + - "16" + descr: Версия PostgreSQL + sort: 160 + is_modifiable: true + - id: 311 + code: jsonParameters + data_type: json + required: true + default: '{ "log_connections": "off", "log_disconnections": "off" }' + maxlength: 2048 + descr: Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html + sort: 170 + is_modifiable: true + - id: 312 + code: enablePgPoolerMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на запись). Будет создан новый под + sort: 180 + is_modifiable: true + - id: 313 + code: enablePgPoolerSlave + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на чтение). Будет создан новый под + sort: 190 + is_modifiable: true + - id: 314 + code: allowNoSSL + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр + sort: 200 + is_modifiable: true + - id: 329 + code: autoScale + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + sort: 300 + - id: 330 + code: autoScalePercentage + data_type: integer > 0 + required: true + default: "10" + value_list: + - "10" + - "15" + - "20" + descr: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + sort: 310 + - id: 331 + code: autoScaleTechWindow + data_type: integer >= 0 + required: true + default: "0" + value_list: + - "0" + - "5" + - "12" + - "23" + descr: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + sort: 320 + - id: 337 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`' + sort: 80 + is_modifiable: true + - id: 338 + code: ipSpaceNameSlave + data_type: string + required: false + descr: Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave + sort: 100 + is_modifiable: true + - id: 339 + code: autoScaleQuotaGb + data_type: string + required: true + default: "1" + descr: Квота для autoScale + sort: 330 + - name: create_database + id: 245 + kind: subresource + action: create + subresource: database + params: + - id: 741 + code: dbName + data_type: string + required: true + - id: 742 + code: dbOwner + data_type: string + required: true + - name: create_user + id: 241 + kind: subresource + action: create + subresource: user + params: + - id: 733 + code: username + data_type: string + required: true + regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ + maxlength: 62 + minlength: 2 + - id: 740 + code: role + data_type: string + required: true + value_list: + - app_user + - ddl_user + descr: Выбрать роль из списка + man: '`app_user` — стандартный пользователь с базовым набором прав
`ddl_user` — пользователь с расширенными правами, включая управление ролями и базами данных' + - name: delete + id: 20 + kind: instance + action: delete + params: + - id: 457 + code: deleteS3Bucket + data_type: boolean + required: true + default: "true" + value_list: + - "false" + - "true" + descr: Также удалить технический S3 Bucket с бэкапами? + sort: 10 + - name: delete_database + id: 246 + kind: subresource + action: delete + subresource: database + params: + - id: 743 + code: dbName + data_type: string + required: true + - name: delete_user + id: 244 + kind: subresource + action: delete + subresource: user + params: + - id: 739 + code: username + data_type: string + required: true + descr: 'Пример: `username-admin`' + - name: modify + id: 48 + kind: instance + action: modify + params: + - id: 91 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 40 + - id: 92 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 20 + - id: 93 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + man: Квота ядра пода (milicores) + sort: 10 + - id: 94 + code: resourceDisk + data_type: integer > 0 + required: false + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 30 + - id: 147 + code: needExternalAddressMaster + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к master + man: Выделяет внешний IP для доступа к master + sort: 50 + - id: 148 + code: needExternalAddressSlave + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к slave. Действует, если slave в принципе есть + man: Выделяет внешний IP для доступа к slave. Действует, если slave в принципе есть + sort: 70 + - id: 267 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + man: Не обязательный. Время запуска бекапа + sort: 90 + - id: 268 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: false + maxvalue: 99 + minvalue: 1 + man: Не обязательный. Количество сохраняемых бекапов + sort: 100 + - id: 315 + code: appVersion + data_type: string + required: true + value_list: + - "17" + - "16" + descr: Версия PostgreSQL. Нельзя опускать вниз + sort: 110 + - id: 316 + code: jsonParameters + data_type: json + required: false + maxlength: 2048 + descr: Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html + sort: 120 + - id: 317 + code: enablePgPoolerMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на запись). Будет создан новый под + sort: 130 + - id: 318 + code: enablePgPoolerSlave + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на чтение). Будет создан новый под + sort: 140 + - id: 319 + code: allowNoSSL + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр + sort: 150 + - id: 541 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`' + sort: 60 + - id: 542 + code: ipSpaceNameSlave + data_type: string + required: false + descr: Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave + sort: 80 + - name: recovery + id: 47 + kind: action + action: recovery + params: + - id: 86 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети
+ sort: 50 + - id: 87 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes)
+ man: Квота памяти пода (Указывается в Megabytes)
+ sort: 30 + - id: 88 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili)
+ man: Квота ядра пода (Указывается в mili)
+ sort: 20 + - id: 89 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 40 + - id: 131 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 10 + - name: restart + id: 54 + kind: action + action: restart + man: ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов.
Может быть полная поломка. Крайне осторожно запускать при работе + params: [] + - name: resume + id: 114 + kind: instance + action: resume + params: [] + - name: suspend + id: 115 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/91_redis.yaml b/generated/dev/resources_yaml/91_redis.yaml new file mode 100644 index 0000000..a710a58 --- /dev/null +++ b/generated/dev/resources_yaml/91_redis.yaml @@ -0,0 +1,118 @@ +name: redis +service_id: 91 +service_display_name: Redis +service_short_name: redis +service_man: 'Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 21 + kind: instance + action: create + params: + - id: 112 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 4000 + minvalue: 500 + descr: Квота ядра пода (Указывается в mili) + man: Квота ядра пода (Указывается в mili) + sort: 20 + - id: 113 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 512 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 30 + - id: 114 + code: resourceDisk + data_type: integer > 0 + required: true + default: "30" + maxvalue: 999 + minvalue: 10 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 40 + - id: 181 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети. Если выделить больше 1 ноды, будет добавлено дополнительно 3 Sentinel ноды + sort: 50 + - id: 182 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения
+ sort: 10 + - id: 183 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к master + man: Выделяет внешний IP для доступа к master + sort: 60 + - id: 184 + code: needExternalAddressSlave + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть + man: Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть + sort: 70 + - id: 548 + code: ipSpaceNameMaster + data_type: string + required: false + descr: Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster + - id: 549 + code: ipSpaceNameSlave + data_type: string + required: false + descr: Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave + - name: delete + id: 22 + kind: instance + action: delete + params: [] diff --git a/generated/dev/resources_yaml/92_mongodb.yaml b/generated/dev/resources_yaml/92_mongodb.yaml new file mode 100644 index 0000000..9f4a36e --- /dev/null +++ b/generated/dev/resources_yaml/92_mongodb.yaml @@ -0,0 +1,185 @@ +name: mongodb +service_id: 92 +service_display_name: MongoDB +service_short_name: mongodb +service_man: '# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание базы
* **delete** — удаление базы данных
* **suspend** — остановка базы данных
* **resume** — запуск базы данных
* **modify** — изменение базы данных

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания базы данных

Эти параметры необходимо указать при создании базы данных.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Оптимально выбирать нечётное количество узлов в кластере MongoDB, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Выделение внешнего IP для доступа к master

Флаг, определяющий необходимость выделения внешнего IP.
Допустимые значения: `true` или `false`

### Имя ipSpace для публикации внешнего IP

Указывается только если включено выделение внешнего IP.
Пример: `internet-no-antiddos-v1`

---

## 3. Параметры модификации базы данных

Все параметры, которые вы указываете при модификации, являются необязательными — система автоматически подставит ранее сохранённые значения, и при необходимости вы можете изменить их на нужные.

### Количество узлов

Оптимально выбирать нечётное количество узлов в кластере MongoDB, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных указывается в гигабайтах. Его можно увеличить при необходимости, однако уменьшить уже выделенный размер невозможно.
Пример: `20`

### Выделение внешнего IP для доступа к master

Флаг, определяющий необходимость выделения внешнего IP.
Допустимые значения: `true` или `false`

### Имя ipSpace для публикации внешнего IP

Указывается только если включено выделение внешнего IP.
Пример: `internet-no-antiddos-v1`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания базы данных платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 37 + kind: instance + action: create + params: + - id: 487 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `2`' + sort: 10 + is_modifiable: true + - id: 588 + code: resourceMemory + data_type: integer > 0 + required: true + minvalue: 500 + descr: 'Пример: `2048`' + is_modifiable: true + - id: 589 + code: resourceCPU + data_type: integer > 0 + required: true + descr: 'Пример: `1000`' + is_modifiable: true + - id: 590 + code: resourceDisk + data_type: integer > 0 + required: true + descr: 'Пример: `20`' + is_modifiable: true + - id: 595 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 10 + - id: 645 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`' + is_modifiable: true + - id: 646 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' + is_modifiable: true + - name: create_user + id: 198 + kind: subresource + action: create + subresource: user + params: + - id: 635 + code: username + data_type: string + required: true + - id: 636 + code: dbName + data_type: string + required: true + - id: 643 + code: role + data_type: string + required: true + value_list: + - dbOwner + - read + - readWrite + - name: delete + id: 190 + kind: instance + action: delete + params: [] + - name: delete_user + id: 199 + kind: subresource + action: delete + subresource: user + params: + - id: 644 + code: username + data_type: string + required: true + - name: modify + id: 201 + kind: instance + action: modify + params: + - id: 648 + code: resourceInstances + data_type: integer > 0 + required: false + descr: 'Пример: `2`' + - id: 649 + code: resourceMemory + data_type: integer > 0 + required: false + descr: 'Пример: `2048`' + - id: 650 + code: resourceCPU + data_type: integer > 0 + required: false + descr: 'Пример: `1000`' + - id: 651 + code: resourceDisk + data_type: integer > 0 + required: false + descr: 'Пример: `20`' + - id: 652 + code: needExternalAddressMaster + data_type: boolean + required: false + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`' + - id: 653 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' + - name: modify_user + id: 200 + kind: subresource + action: modify + subresource: user + params: + - id: 640 + code: username + data_type: string + required: true + - id: 641 + code: dbName + data_type: string + required: true + - id: 642 + code: role + data_type: string + required: true + value_list: + - dbOwner + - read + - readWrite + - name: resume + id: 196 + kind: instance + action: resume + params: [] + - name: suspend + id: 194 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/93_rabbitmq.yaml b/generated/dev/resources_yaml/93_rabbitmq.yaml similarity index 100% rename from provider/resources_yaml/93_rabbitmq.yaml rename to generated/dev/resources_yaml/93_rabbitmq.yaml diff --git a/generated/dev/resources_yaml/94_lucee.yaml b/generated/dev/resources_yaml/94_lucee.yaml new file mode 100644 index 0000000..5ec0c17 --- /dev/null +++ b/generated/dev/resources_yaml/94_lucee.yaml @@ -0,0 +1,217 @@ +name: lucee +service_id: 94 +service_display_name: Lucee +service_short_name: lucee +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 49 + kind: instance + action: create + params: + - id: 103 + code: domain + data_type: string + required: true + default: lucee- + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 104 + code: gitPath + data_type: string + required: true + default: https://github.com/xahys/testlucee + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 60 + is_modifiable: true + - id: 105 + code: jsonEnv + data_type: json + required: false + default: '{ "param": "value" }' + maxlength: 2048 + descr: Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения. + sort: 70 + is_modifiable: true + - id: 106 + code: resourceCPU + data_type: integer > 0 + required: true + default: "300" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + man: Квота ядра пода (milicores) + sort: 30 + is_modifiable: true + - id: 107 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 108 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 132 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код + sort: 80 + is_modifiable: true + - id: 139 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 50 + is_modifiable: true + - id: 263 + code: appVersion + data_type: string + required: true + default: "5.4" + value_list: + - "5.4" + - "6.0" + regex: ^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$ + descr: Версия Lucee + sort: 90 + is_modifiable: true + - name: delete + id: 50 + kind: instance + action: delete + params: [] + - name: modify + id: 55 + kind: instance + action: modify + man: Если CFS оставить пустым, то при запуске джобы берётся параметр из instanceData + params: + - id: 133 + code: gitPath + data_type: string + required: false + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 50 + - id: 134 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + sort: 60 + - id: 135 + code: jsonEnv + data_type: json + required: false + maxlength: 2048 + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 40 + - id: 136 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) 1000Mili = 1 ядро + man: Квота ядра пода (Указывается в mili) + sort: 10 + - id: 137 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 20 + - id: 138 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 30 + - id: 264 + code: appVersion + data_type: string + required: true + default: "5.4" + value_list: + - "5.4" + - "6.0" + regex: ^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$ + sort: 70 + - name: redeploy + id: 53 + kind: action + action: redeploy + params: + - id: 257 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: restart + id: 163 + kind: action + action: restart + params: [] + - name: resume + id: 95 + kind: instance + action: resume + params: [] + - name: suspend + id: 96 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/95_nodejs.yaml b/generated/dev/resources_yaml/95_nodejs.yaml new file mode 100644 index 0000000..c387870 --- /dev/null +++ b/generated/dev/resources_yaml/95_nodejs.yaml @@ -0,0 +1,215 @@ +name: nodejs +service_id: 95 +service_display_name: NodeJS +service_short_name: nodejs +service_man: Развертывает контейнер в k8s кластере с node, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 58 + kind: instance + action: create + params: + - id: 149 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Наименование поддомена, будет добавлено в nodejs.dev.nubes.ru + sort: 10 + - id: 150 + code: gitPath + data_type: string + required: true + default: https://github.com/xahys/testnode.git + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 60 + is_modifiable: true + - id: 151 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет + sort: 80 + is_modifiable: true + - id: 152 + code: jsonEnv + data_type: json + required: false + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 70 + is_modifiable: true + - id: 153 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores). 1000Mili = 1 Ядро + man: Квота ядра пода (milicores). 1000Mili = 1 Ядро + sort: 30 + is_modifiable: true + - id: 154 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 155 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 50 + is_modifiable: true + - id: 156 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 20 + - id: 270 + code: appVersion + data_type: string + required: true + default: "23" + value_list: + - "18" + - "20" + - "22" + - "23" + regex: ^(\d){2}$ + descr: Версия приложения + sort: 90 + is_modifiable: true + - name: delete + id: 61 + kind: instance + action: delete + params: [] + - name: modify + id: 59 + kind: instance + action: modify + params: + - id: 157 + code: gitPath + data_type: string + required: false + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 50 + - id: 158 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + sort: 60 + - id: 159 + code: jsonEnv + data_type: json + required: false + maxlength: 2048 + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 40 + - id: 160 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) 1000Mili = 1 ядро + man: Квота ядра пода (Указывается в mili) 1000Mili = 1 ядро + sort: 10 + - id: 161 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 20 + - id: 162 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 30 + - id: 271 + code: appVersion + data_type: string + required: true + default: "23" + value_list: + - "18" + - "20" + - "22" + - "23" + regex: ^(\d){2}$ + descr: Версия приложения + man: Версия приложения + sort: 70 + - name: redeploy + id: 60 + kind: action + action: redeploy + params: + - id: 258 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: resume + id: 121 + kind: instance + action: resume + params: [] + - name: suspend + id: 120 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/96_pgadmin.yaml b/generated/dev/resources_yaml/96_pgadmin.yaml new file mode 100644 index 0000000..336df63 --- /dev/null +++ b/generated/dev/resources_yaml/96_pgadmin.yaml @@ -0,0 +1,158 @@ +name: pgadmin +service_id: 96 +service_display_name: pgAdmin +service_short_name: pgadmin +service_man: Инструмент с графическим интерфейсом для управления базами данных PostgreSQL +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 62 + kind: instance + action: create + params: + - id: 164 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 165 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 500 + descr: Квота ядра пода (milicores). 1000Mili = 1 Ядро + man: Квота ядра пода (milicores). 1000Mili = 1 Ядро + sort: 30 + is_modifiable: true + - id: 166 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 512 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 167 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 169 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 170 + code: login + data_type: string + required: true + regex: ^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$ + maxlength: 255 + minlength: 3 + descr: Логин для подключения к pgAdmin, в формате email (login@email.net) + man: Логин для подключения к pgAdmin, в формате email (login@email.net) + sort: 60 + - id: 171 + code: password + data_type: string + required: true + descr: Пароль для подключения к pgAdmin + man: Пароль для подключения к pgAdmin + sort: 70 + is_sensitive: true + - name: delete + id: 63 + kind: instance + action: delete + params: [] + - name: modify + id: 64 + kind: instance + action: modify + params: + - id: 172 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + - id: 173 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + - id: 174 + code: resourceDisk + data_type: integer > 0 + required: false + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + - name: redeploy + id: 65 + kind: action + action: redeploy + params: + - id: 277 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: resume + id: 123 + kind: instance + action: resume + params: [] + - name: suspend + id: 122 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/97_nodered.yaml b/generated/dev/resources_yaml/97_nodered.yaml similarity index 100% rename from provider/resources_yaml/97_nodered.yaml rename to generated/dev/resources_yaml/97_nodered.yaml diff --git a/generated/dev/resources_yaml/98_http.yaml b/generated/dev/resources_yaml/98_http.yaml new file mode 100644 index 0000000..58a9db9 --- /dev/null +++ b/generated/dev/resources_yaml/98_http.yaml @@ -0,0 +1,150 @@ +name: http +service_id: 98 +service_display_name: Простой HTTP контейнер +service_short_name: http +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 77 + kind: instance + action: create + params: + - id: 212 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 213 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) + man: Квота ядра пода (Указывается в mili) + sort: 30 + is_modifiable: true + - id: 214 + code: resourceMemory + data_type: integer > 0 + required: true + default: "200" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 40 + is_modifiable: true + - id: 215 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robin + sort: 50 + is_modifiable: true + - id: 216 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 217 + code: registryPath + data_type: string + required: true + default: nexus-sa.tst.nubes.ru/docker-nubes/jolt + descr: Путь до image, который будет установлен как deployment + man: Путь до image, который будет установлен как deployment + sort: 60 + - id: 241 + code: jsonEnv + data_type: json + required: false + default: '{"param": "value"}' + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 70 + - name: delete + id: 78 + kind: instance + action: delete + params: [] + - name: modify + id: 81 + kind: instance + action: modify + params: + - id: 218 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 4000 + minvalue: 100 + - id: 219 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 128 + - id: 220 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + - name: redeploy + id: 79 + kind: action + action: redeploy + params: + - id: 272 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 10 + - name: resume + id: 130 + kind: instance + action: resume + params: [] + - name: suspend + id: 131 + kind: instance + action: suspend + params: [] diff --git a/generated/dev/resources_yaml/99_gitea.yaml b/generated/dev/resources_yaml/99_gitea.yaml new file mode 100644 index 0000000..87f9cb9 --- /dev/null +++ b/generated/dev/resources_yaml/99_gitea.yaml @@ -0,0 +1,153 @@ +name: gitea +service_id: 99 +service_display_name: Gitea +service_short_name: gitea +service_man: Развертывает сервис контроля версий Gitea
Зависит от сервиса postgresql который должен быть развернут заранее. +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 82 + kind: instance + action: create + params: + - id: 232 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 300 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 40 + is_modifiable: true + - id: 233 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 256 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 50 + is_modifiable: true + - id: 234 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 60 + is_modifiable: true + - id: 235 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 70 + is_modifiable: true + - id: 237 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 20 + - id: 238 + code: psqlUid + data_type: uuid + required: true + ref_svc_id: 90 + descr: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически + man: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически + sort: 10 + - name: delete + id: 83 + kind: instance + action: delete + params: [] + - name: modify + id: 91 + kind: instance + action: modify + params: + - id: 259 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 500 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 10 + - id: 260 + code: resourceMemory + data_type: integer > 0 + required: true + default: "128" + maxvalue: 8192 + minvalue: 256 + descr: Квота памяти пода (Указывается в Megabytes) + sort: 20 + - id: 261 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 30 + - id: 262 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 40 + - name: resume + id: 127 + kind: instance + action: resume + params: [] + - name: suspend + id: 126 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/embed.go b/generated/dev/resources_yaml/embed.go similarity index 88% rename from provider/resources_yaml/embed.go rename to generated/dev/resources_yaml/embed.go index b326ff8..72e3ff2 100644 --- a/provider/resources_yaml/embed.go +++ b/generated/dev/resources_yaml/embed.go @@ -4,5 +4,5 @@ import "embed" // Files embeds unified service YAML specs for provider runtime lookup. // -//go:embed * +//go:embed *.yaml var Files embed.FS diff --git a/generated/dev/tmp/.gitkeep b/generated/dev/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/prod/docs/.gitkeep b/generated/prod/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/prod/docs/akhq.md b/generated/prod/docs/akhq.md new file mode 100644 index 0000000..b160ca3 --- /dev/null +++ b/generated/prod/docs/akhq.md @@ -0,0 +1,13 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## MAN + +
+# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group'ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
--- +
diff --git a/generated/prod/docs/akhq_example.md b/generated/prod/docs/akhq_example.md new file mode 100644 index 0000000..71302f1 --- /dev/null +++ b/generated/prod/docs/akhq_example.md @@ -0,0 +1,50 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Copy-ready manifest (`akhq_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_akhq" "baza" { + resource_realm = "TODO" # Выбрать платформу из списка + domain = "TODO" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + kafka_uid = "TODO" # Выбрать кластер из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_memory = 512 # Пример: `1024` + resource_cpu = 500 # Пример: `1000` + resource_instances = 1 +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_akhq.baza.state_params["имя_ключа"] +nubes_akhq.baza.state_out["имя_ключа"] +nubes_akhq.baza.state_params_flat["имя_ключа"] +nubes_akhq.baza.state_out_flat["имя_ключа"] +nubes_akhq.baza.vault_secrets["имя_ключа"] +nubes_akhq.baza.vault_url +nubes_akhq.baza.vault_user_path +nubes_akhq.baza.vault_fields +``` diff --git a/generated/prod/docs/akhq_ops.md b/generated/prod/docs/akhq_ops.md new file mode 100644 index 0000000..9e4364f --- /dev/null +++ b/generated/prod/docs/akhq_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](akhq_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](akhq_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/akhq_outputs.md b/generated/prod/docs/akhq_outputs.md new file mode 100644 index 0000000..22405f5 --- /dev/null +++ b/generated/prod/docs/akhq_outputs.md @@ -0,0 +1,33 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` + +### `vault_secrets` ключи + +- `vault_secrets["pass"]` +- `vault_secrets["user"]` + diff --git a/generated/prod/docs/akhq_params.md b/generated/prod/docs/akhq_params.md new file mode 100644 index 0000000..b419cf6 --- /dev/null +++ b/generated/prod/docs/akhq_params.md @@ -0,0 +1,12 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Parameters + +- [Create params](akhq_params_create.md) +- [Modify params](akhq_params_modify.md) diff --git a/generated/prod/docs/akhq_params_create.md b/generated/prod/docs/akhq_params_create.md new file mode 100644 index 0000000..687ca12 --- /dev/null +++ b/generated/prod/docs/akhq_params_create.md @@ -0,0 +1,40 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
550resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
553domainstringЕсли будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
555kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
551resource_memoryinteger512Пример: `1024`minvalue=512
552resource_cpuinteger500Пример: `1000`minvalue=500
554resource_instancesinteger1minvalue=1
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/akhq_params_modify.md b/generated/prod/docs/akhq_params_modify.md new file mode 100644 index 0000000..1ba2657 --- /dev/null +++ b/generated/prod/docs/akhq_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
565kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
diff --git a/generated/prod/docs/dnsrecord.md b/generated/prod/docs/dnsrecord.md new file mode 100644 index 0000000..22f9734 --- /dev/null +++ b/generated/prod/docs/dnsrecord.md @@ -0,0 +1,13 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## MAN + +
+Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи +
diff --git a/generated/prod/docs/dnsrecord_example.md b/generated/prod/docs/dnsrecord_example.md new file mode 100644 index 0000000..4f3667e --- /dev/null +++ b/generated/prod/docs/dnsrecord_example.md @@ -0,0 +1,49 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Copy-ready manifest (`dnsrecord_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_dnsrecord" "baza" { + zone_uid = "TODO" # UUID экземпляра услуги DNS-зоны + record_type = "A" # Тип DNS-записи + record_name = "TODO" # Имя записи. Имя зоны в конце проставляется автоматически + record_input = "TODO" # Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + record_ttl = 3600 # Time-to-Leave записи. Раз в какое время запись будет обновлена +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dnsrecord.baza.state_params["имя_ключа"] +nubes_dnsrecord.baza.state_out["имя_ключа"] +nubes_dnsrecord.baza.state_params_flat["имя_ключа"] +nubes_dnsrecord.baza.state_out_flat["имя_ключа"] +nubes_dnsrecord.baza.vault_secrets["имя_ключа"] +nubes_dnsrecord.baza.vault_url +nubes_dnsrecord.baza.vault_user_path +nubes_dnsrecord.baza.vault_fields +``` diff --git a/generated/prod/docs/dnsrecord_ops.md b/generated/prod/docs/dnsrecord_ops.md new file mode 100644 index 0000000..475844c --- /dev/null +++ b/generated/prod/docs/dnsrecord_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dnsrecord_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](dnsrecord_params_modify.md) diff --git a/generated/prod/docs/dnsrecord_outputs.md b/generated/prod/docs/dnsrecord_outputs.md new file mode 100644 index 0000000..4d127c2 --- /dev/null +++ b/generated/prod/docs/dnsrecord_outputs.md @@ -0,0 +1,29 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dnsServers"]` +- `state_out_flat["record"]` + diff --git a/generated/prod/docs/dnsrecord_params.md b/generated/prod/docs/dnsrecord_params.md new file mode 100644 index 0000000..84701f9 --- /dev/null +++ b/generated/prod/docs/dnsrecord_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Parameters + +- [Create params](dnsrecord_params_create.md) +- [Modify params](dnsrecord_params_modify.md) diff --git a/generated/prod/docs/dnsrecord_params_create.md b/generated/prod/docs/dnsrecord_params_create.md new file mode 100644 index 0000000..b3a67ca --- /dev/null +++ b/generated/prod/docs/dnsrecord_params_create.md @@ -0,0 +1,27 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
75zone_uiduuidUUID экземпляра услуги DNS-зоныref_svc_id=110
76record_typestringТип DNS-записиvalue_list=A, AAAA, CNAME, MX, PTR, SOA, SRV, TXT, LUA, NS, SSHFP
77record_namestringИмя записи. Имя зоны в конце проставляется автоматическиunique_scope=parent
78record_inputstringЗначение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
450record_ttlinteger3600Time-to-Leave записи. Раз в какое время запись будет обновленаminvalue=30; maxvalue=604800
diff --git a/generated/prod/docs/dnsrecord_params_modify.md b/generated/prod/docs/dnsrecord_params_modify.md new file mode 100644 index 0000000..d3cdbdb --- /dev/null +++ b/generated/prod/docs/dnsrecord_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
256record_inputstringЗапись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP
diff --git a/generated/prod/docs/dnszone.md b/generated/prod/docs/dnszone.md new file mode 100644 index 0000000..f8249dd --- /dev/null +++ b/generated/prod/docs/dnszone.md @@ -0,0 +1,13 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## MAN + +
+## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
``` +
diff --git a/generated/prod/docs/dnszone_example.md b/generated/prod/docs/dnszone_example.md new file mode 100644 index 0000000..04c2bca --- /dev/null +++ b/generated/prod/docs/dnszone_example.md @@ -0,0 +1,52 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Copy-ready manifest (`dnszone_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_dnszone" "baza" { + zone_email = "TODO" # Контактный e-mail + zone_name = "TODO" # Имя зоны + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + zone_kind = "NATIVE" # Тип зоны + zone_time_to_refresh = 86400 # Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов + zone_time_to_retry = 7200 # Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов + zone_time_to_expire = 3600000 # Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным + zone_time_to_live = 86400 # Время, в течение которого информация будет кешироваться другими DNS-серверами +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dnszone.baza.state_params["имя_ключа"] +nubes_dnszone.baza.state_out["имя_ключа"] +nubes_dnszone.baza.state_params_flat["имя_ключа"] +nubes_dnszone.baza.state_out_flat["имя_ключа"] +nubes_dnszone.baza.vault_secrets["имя_ключа"] +nubes_dnszone.baza.vault_url +nubes_dnszone.baza.vault_user_path +nubes_dnszone.baza.vault_fields +``` diff --git a/generated/prod/docs/dnszone_ops.md b/generated/prod/docs/dnszone_ops.md new file mode 100644 index 0000000..e07344c --- /dev/null +++ b/generated/prod/docs/dnszone_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dnszone_params_create.md) +- `delete` diff --git a/generated/prod/docs/dnszone_outputs.md b/generated/prod/docs/dnszone_outputs.md new file mode 100644 index 0000000..0c3556d --- /dev/null +++ b/generated/prod/docs/dnszone_outputs.md @@ -0,0 +1,32 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dnsServers"]` +- `state_out_flat["dnsServers.ns1.nubes.ru"]` +- `state_out_flat["dnsServers.ns2.nubes.ru"]` +- `state_out_flat["zoneSerial"]` +- `state_out_flat["zoneSoaServer"]` + diff --git a/generated/prod/docs/dnszone_params.md b/generated/prod/docs/dnszone_params.md new file mode 100644 index 0000000..0f1f739 --- /dev/null +++ b/generated/prod/docs/dnszone_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Parameters + +- [Create params](dnszone_params_create.md) +- [Modify params](dnszone_params_modify.md) diff --git a/generated/prod/docs/dnszone_params_create.md b/generated/prod/docs/dnszone_params_create.md new file mode 100644 index 0000000..0690a8f --- /dev/null +++ b/generated/prod/docs/dnszone_params_create.md @@ -0,0 +1,31 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
66zone_emailstringКонтактный e-mail
67zone_namestringИмя зоныregex=^([a-z0-9-]+\.)+[a-z0-9-]+$
69zone_masters_for_slavestringИспользовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов
284resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
68zone_kindstringNATIVEТип зоныvalue_list=NATIVE, SLAVE
70zone_time_to_refreshstring86400Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов
71zone_time_to_retrystring7200Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов
72zone_time_to_expirestring3600000Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным
73zone_time_to_livestring86400Время, в течение которого информация будет кешироваться другими DNS-серверами
diff --git a/generated/prod/docs/dnszone_params_modify.md b/generated/prod/docs/dnszone_params_modify.md new file mode 100644 index 0000000..bd1dc13 --- /dev/null +++ b/generated/prod/docs/dnszone_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/dummy.md b/generated/prod/docs/dummy.md new file mode 100644 index 0000000..53f9855 --- /dev/null +++ b/generated/prod/docs/dummy.md @@ -0,0 +1,13 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## MAN + +
+## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
``` +
diff --git a/generated/prod/docs/dummy_example.md b/generated/prod/docs/dummy_example.md new file mode 100644 index 0000000..4f60ba3 --- /dev/null +++ b/generated/prod/docs/dummy_example.md @@ -0,0 +1,49 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Copy-ready manifest (`dummy_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_dummy" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + duration_ms = 0 # перед завершением подождать (sleep) столько миллисекунд + fail_at_start = false # если true, завершить джоб, ничего не записывая в cmdb + fail_in_progress = false # если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + where_fail = 1 # принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dummy.baza.state_params["имя_ключа"] +nubes_dummy.baza.state_out["имя_ключа"] +nubes_dummy.baza.state_params_flat["имя_ключа"] +nubes_dummy.baza.state_out_flat["имя_ключа"] +nubes_dummy.baza.vault_secrets["имя_ключа"] +nubes_dummy.baza.vault_url +nubes_dummy.baza.vault_user_path +nubes_dummy.baza.vault_fields +``` diff --git a/generated/prod/docs/dummy_ops.md b/generated/prod/docs/dummy_ops.md new file mode 100644 index 0000000..2cd4d8f --- /dev/null +++ b/generated/prod/docs/dummy_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dummy_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](dummy_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/prod/docs/dummy_outputs.md b/generated/prod/docs/dummy_outputs.md new file mode 100644 index 0000000..52b99e1 --- /dev/null +++ b/generated/prod/docs/dummy_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `vault_secrets` ключи + +- `vault_secrets["someKey"]` + diff --git a/generated/prod/docs/dummy_params.md b/generated/prod/docs/dummy_params.md new file mode 100644 index 0000000..67ae088 --- /dev/null +++ b/generated/prod/docs/dummy_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Parameters + +- [Create params](dummy_params_create.md) +- [Modify params](dummy_params_modify.md) diff --git a/generated/prod/docs/dummy_params_create.md b/generated/prod/docs/dummy_params_create.md new file mode 100644 index 0000000..a098d0b --- /dev/null +++ b/generated/prod/docs/dummy_params_create.md @@ -0,0 +1,46 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
242resource_realmstringНаименование ресурсной платформы для подключенияfunc=getAvailableResourceRealms
286bodymessagestringДата, уходящая в Vault
321map_examplemap
322json_examplejsonДля тестирования интерфейса. Сюда можно писать корректный json
396nested_ref_example
447yaml_exampleyaml
647map_fixedmap-fixedНовый тип поля
654array_map_fixed_examplearray-map-fixedArray of map-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
198duration_msinteger0перед завершением подождать (sleep) столько миллисекунд
199fail_at_startbooleanfalse**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой
value_list=false, true
200fail_in_progressbooleanfalseесли true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdbvalue_list=false, true
201where_failinteger1принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault writevalue_list=1, 2, 3
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/dummy_params_modify.md b/generated/prod/docs/dummy_params_modify.md new file mode 100644 index 0000000..b601221 --- /dev/null +++ b/generated/prod/docs/dummy_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
287duration_msintegerперед завершением подождать (sleep) столько миллисекунд
288fail_at_startbooleanесли true, завершить джоб, ничего не записывая в cmdbvalue_list=false, true
289fail_in_progressbooleanесли true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdbvalue_list=false, true
290where_failintegerпринимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault writevalue_list=1, 2, 3
291bodymessageДата, уходящая в Vault
373map_examplemap
374json_example
diff --git a/generated/prod/docs/flask.md b/generated/prod/docs/flask.md new file mode 100644 index 0000000..fc5aa48 --- /dev/null +++ b/generated/prod/docs/flask.md @@ -0,0 +1,13 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/flask_example.md b/generated/prod/docs/flask_example.md new file mode 100644 index 0000000..de97f0c --- /dev/null +++ b/generated/prod/docs/flask_example.md @@ -0,0 +1,51 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Copy-ready manifest (`flask_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_flask" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 300 # Квота ядра пода (milicores) + resource_memory = 256 # Квота памяти пода (MBytes) + resource_instances = 1 # Количество узлов + git_path = "https://github.com/Foxyhhd/Baldurs-Gate-test.git" # Путь до Git, который будет webroot для сайта + json_env = { "param": "value" } # Массив переменных, будут переданы в env приложения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_flask.baza.state_params["имя_ключа"] +nubes_flask.baza.state_out["имя_ключа"] +nubes_flask.baza.state_params_flat["имя_ключа"] +nubes_flask.baza.state_out_flat["имя_ключа"] +nubes_flask.baza.vault_secrets["имя_ключа"] +nubes_flask.baza.vault_url +nubes_flask.baza.vault_user_path +nubes_flask.baza.vault_fields +``` diff --git a/generated/prod/docs/flask_ops.md b/generated/prod/docs/flask_ops.md new file mode 100644 index 0000000..0682476 --- /dev/null +++ b/generated/prod/docs/flask_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](flask_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](flask_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/flask_outputs.md b/generated/prod/docs/flask_outputs.md new file mode 100644 index 0000000..a88f00a --- /dev/null +++ b/generated/prod/docs/flask_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/flask_params.md b/generated/prod/docs/flask_params.md new file mode 100644 index 0000000..608ce56 --- /dev/null +++ b/generated/prod/docs/flask_params.md @@ -0,0 +1,12 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Parameters + +- [Create params](flask_params_create.md) +- [Modify params](flask_params_modify.md) diff --git a/generated/prod/docs/flask_params_create.md b/generated/prod/docs/flask_params_create.md new file mode 100644 index 0000000..f063ce2 --- /dev/null +++ b/generated/prod/docs/flask_params_create.md @@ -0,0 +1,42 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
249resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
255health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
248domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
250resource_cpuinteger300Квота ядра пода (milicores)minvalue=100; maxvalue=4000
251resource_memoryinteger256Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
252resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
253git_pathstringhttps://github.com/Foxyhhd/Baldurs-Gate-test.gitПуть до Git, который будет webroot для сайта
254json_envstring{ &quot;param&quot;: &quot;value&quot; }Массив переменных, будут переданы в env приложения
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/flask_params_modify.md b/generated/prod/docs/flask_params_modify.md new file mode 100644 index 0000000..75a0716 --- /dev/null +++ b/generated/prod/docs/flask_params_modify.md @@ -0,0 +1,19 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Modify params + + + + + + + + + +
IDCodeTypeDescriptionConstraints
451resource_cpuintegerКвота ядра пода (milicores)minvalue=100; maxvalue=4000
452resource_memoryintegerКвота памяти пода (MBytes)minvalue=128; maxvalue=8192
453resource_instancesintegerКоличество узловminvalue=1; maxvalue=7
454git_pathПуть до Git, который будет webroot для сайта
455json_envjsonМассив переменных, будут переданы в env приложения
diff --git a/generated/prod/docs/gitea.md b/generated/prod/docs/gitea.md new file mode 100644 index 0000000..b0fabf5 --- /dev/null +++ b/generated/prod/docs/gitea.md @@ -0,0 +1,13 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## MAN + +
+Развертывает сервис контроля версий Gitea
Зависит от сервиса postgresql который должен быть развернут заранее. +
diff --git a/generated/prod/docs/gitea_complex.md b/generated/prod/docs/gitea_complex.md new file mode 100644 index 0000000..b67da5e --- /dev/null +++ b/generated/prod/docs/gitea_complex.md @@ -0,0 +1,13 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## MAN + +
+Комплексная услуга из ["gittea", "postgres"] +
diff --git a/generated/prod/docs/gitea_complex_example.md b/generated/prod/docs/gitea_complex_example.md new file mode 100644 index 0000000..e795ac0 --- /dev/null +++ b/generated/prod/docs/gitea_complex_example.md @@ -0,0 +1,42 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Copy-ready manifest (`gitea_complex_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_gitea_complex" "baza" { +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_gitea_complex.baza.state_params["имя_ключа"] +nubes_gitea_complex.baza.state_out["имя_ключа"] +nubes_gitea_complex.baza.state_params_flat["имя_ключа"] +nubes_gitea_complex.baza.state_out_flat["имя_ключа"] +nubes_gitea_complex.baza.vault_secrets["имя_ключа"] +nubes_gitea_complex.baza.vault_url +nubes_gitea_complex.baza.vault_user_path +nubes_gitea_complex.baza.vault_fields +``` diff --git a/generated/prod/docs/gitea_complex_ops.md b/generated/prod/docs/gitea_complex_ops.md new file mode 100644 index 0000000..4612ff3 --- /dev/null +++ b/generated/prod/docs/gitea_complex_ops.md @@ -0,0 +1,10 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Operations + diff --git a/generated/prod/docs/gitea_complex_outputs.md b/generated/prod/docs/gitea_complex_outputs.md new file mode 100644 index 0000000..f0593c8 --- /dev/null +++ b/generated/prod/docs/gitea_complex_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/gitea_complex_params.md b/generated/prod/docs/gitea_complex_params.md new file mode 100644 index 0000000..ad58e7f --- /dev/null +++ b/generated/prod/docs/gitea_complex_params.md @@ -0,0 +1,12 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Parameters + +- [Create params](gitea_complex_params_create.md) +- [Modify params](gitea_complex_params_modify.md) diff --git a/generated/prod/docs/gitea_complex_params_create.md b/generated/prod/docs/gitea_complex_params_create.md new file mode 100644 index 0000000..2f57dfd --- /dev/null +++ b/generated/prod/docs/gitea_complex_params_create.md @@ -0,0 +1,16 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + +None. + +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/prod/docs/gitea_complex_params_modify.md b/generated/prod/docs/gitea_complex_params_modify.md new file mode 100644 index 0000000..472dee0 --- /dev/null +++ b/generated/prod/docs/gitea_complex_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/gitea_example.md b/generated/prod/docs/gitea_example.md new file mode 100644 index 0000000..3cea75b --- /dev/null +++ b/generated/prod/docs/gitea_example.md @@ -0,0 +1,50 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Copy-ready manifest (`gitea_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_gitea" "baza" { + psql_uid = "TODO" # UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_gitea.baza.state_params["имя_ключа"] +nubes_gitea.baza.state_out["имя_ключа"] +nubes_gitea.baza.state_params_flat["имя_ключа"] +nubes_gitea.baza.state_out_flat["имя_ключа"] +nubes_gitea.baza.vault_secrets["имя_ключа"] +nubes_gitea.baza.vault_url +nubes_gitea.baza.vault_user_path +nubes_gitea.baza.vault_fields +``` diff --git a/generated/prod/docs/gitea_ops.md b/generated/prod/docs/gitea_ops.md new file mode 100644 index 0000000..0b22aac --- /dev/null +++ b/generated/prod/docs/gitea_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](gitea_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](gitea_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/gitea_outputs.md b/generated/prod/docs/gitea_outputs.md new file mode 100644 index 0000000..3944814 --- /dev/null +++ b/generated/prod/docs/gitea_outputs.md @@ -0,0 +1,39 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` + diff --git a/generated/prod/docs/gitea_params.md b/generated/prod/docs/gitea_params.md new file mode 100644 index 0000000..f0931ff --- /dev/null +++ b/generated/prod/docs/gitea_params.md @@ -0,0 +1,12 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Parameters + +- [Create params](gitea_params_create.md) +- [Modify params](gitea_params_modify.md) diff --git a/generated/prod/docs/gitea_params_create.md b/generated/prod/docs/gitea_params_create.md new file mode 100644 index 0000000..378430a --- /dev/null +++ b/generated/prod/docs/gitea_params_create.md @@ -0,0 +1,40 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
238psql_uiduuidUUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматическиref_svc_id=90
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
232resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=300; maxvalue=4000
233resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=256; maxvalue=8192
234resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
235resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
237domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/gitea_params_modify.md b/generated/prod/docs/gitea_params_modify.md new file mode 100644 index 0000000..f584096 --- /dev/null +++ b/generated/prod/docs/gitea_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
259resource_cpuintegerКвота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=500; maxvalue=4000
260resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=256; maxvalue=8192
261resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
262resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
diff --git a/generated/prod/docs/harbor.md b/generated/prod/docs/harbor.md new file mode 100644 index 0000000..c0cad0a --- /dev/null +++ b/generated/prod/docs/harbor.md @@ -0,0 +1,13 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## MAN + +
+Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak +
diff --git a/generated/prod/docs/harbor_example.md b/generated/prod/docs/harbor_example.md new file mode 100644 index 0000000..5220c95 --- /dev/null +++ b/generated/prod/docs/harbor_example.md @@ -0,0 +1,51 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Copy-ready manifest (`harbor_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_harbor" "baza" { + resource_realm = "TODO" # Платформа для развертывания + s3_uid = "TODO" # UUID экземпляра услуги S3 + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + emails = "testuser1" # Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел + resource_cpu = 100 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 200 # Квота памяти пода (Указывается в Megabytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_harbor.baza.state_params["имя_ключа"] +nubes_harbor.baza.state_out["имя_ключа"] +nubes_harbor.baza.state_params_flat["имя_ключа"] +nubes_harbor.baza.state_out_flat["имя_ключа"] +nubes_harbor.baza.vault_secrets["имя_ключа"] +nubes_harbor.baza.vault_url +nubes_harbor.baza.vault_user_path +nubes_harbor.baza.vault_fields +``` diff --git a/generated/prod/docs/harbor_ops.md b/generated/prod/docs/harbor_ops.md new file mode 100644 index 0000000..9a06a19 --- /dev/null +++ b/generated/prod/docs/harbor_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](harbor_params_create.md) +- `delete` — DEV-Ветка удаления Harbor diff --git a/generated/prod/docs/harbor_outputs.md b/generated/prod/docs/harbor_outputs.md new file mode 100644 index 0000000..3422bbd --- /dev/null +++ b/generated/prod/docs/harbor_outputs.md @@ -0,0 +1,40 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.k8sregistry"]` +- `state_out_flat["monitoring.k8sregistry.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` + diff --git a/generated/prod/docs/harbor_params.md b/generated/prod/docs/harbor_params.md new file mode 100644 index 0000000..6819254 --- /dev/null +++ b/generated/prod/docs/harbor_params.md @@ -0,0 +1,12 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Parameters + +- [Create params](harbor_params_create.md) +- [Modify params](harbor_params_modify.md) diff --git a/generated/prod/docs/harbor_params_create.md b/generated/prod/docs/harbor_params_create.md new file mode 100644 index 0000000..d5f1c60 --- /dev/null +++ b/generated/prod/docs/harbor_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
110resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
231s3_uiduuidUUID экземпляра услуги S3ref_svc_id=12
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
16domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
17emailsstringtestuser1Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел
228resource_cpuinteger100Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
229resource_memoryinteger200Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
230resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
diff --git a/generated/prod/docs/harbor_params_modify.md b/generated/prod/docs/harbor_params_modify.md new file mode 100644 index 0000000..0960de7 --- /dev/null +++ b/generated/prod/docs/harbor_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/http.md b/generated/prod/docs/http.md new file mode 100644 index 0000000..68075b0 --- /dev/null +++ b/generated/prod/docs/http.md @@ -0,0 +1,13 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/http_example.md b/generated/prod/docs/http_example.md new file mode 100644 index 0000000..1c12225 --- /dev/null +++ b/generated/prod/docs/http_example.md @@ -0,0 +1,51 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Copy-ready manifest (`http_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_http" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 100 # Квота ядра пода (Указывается в mili) + resource_memory = 200 # Квота памяти пода (Указывается в Megabytes) + resource_instances = 1 # Количество узлов + registry_path = "nexus-sa.tst.nubes.ru/docker-nubes/jolt" # Путь до image, который будет установлен как deployment + json_env = {"param": "value"} # Массив переменных, будут переданы в env приложения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_http.baza.state_params["имя_ключа"] +nubes_http.baza.state_out["имя_ключа"] +nubes_http.baza.state_params_flat["имя_ключа"] +nubes_http.baza.state_out_flat["имя_ключа"] +nubes_http.baza.vault_secrets["имя_ключа"] +nubes_http.baza.vault_url +nubes_http.baza.vault_user_path +nubes_http.baza.vault_fields +``` diff --git a/generated/prod/docs/http_ops.md b/generated/prod/docs/http_ops.md new file mode 100644 index 0000000..547defd --- /dev/null +++ b/generated/prod/docs/http_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](http_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](http_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/prod/docs/http_outputs.md b/generated/prod/docs/http_outputs.md new file mode 100644 index 0000000..9baf737 --- /dev/null +++ b/generated/prod/docs/http_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/prod/docs/http_params.md b/generated/prod/docs/http_params.md new file mode 100644 index 0000000..07aa372 --- /dev/null +++ b/generated/prod/docs/http_params.md @@ -0,0 +1,12 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Parameters + +- [Create params](http_params_create.md) +- [Modify params](http_params_modify.md) diff --git a/generated/prod/docs/http_params_create.md b/generated/prod/docs/http_params_create.md new file mode 100644 index 0000000..aa7d336 --- /dev/null +++ b/generated/prod/docs/http_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
216resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
212domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
213resource_cpuinteger100Квота ядра пода (Указывается в mili)minvalue=100; maxvalue=4000
214resource_memoryinteger200Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
215resource_instancesinteger1Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robinminvalue=1; maxvalue=7
217registry_pathstringnexus-sa.tst.nubes.ru/docker-nubes/joltПуть до image, который будет установлен как deployment
241json_envjson{&quot;param&quot;: &quot;value&quot;}Массив переменных, будут переданы в env приложения
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/http_params_modify.md b/generated/prod/docs/http_params_modify.md new file mode 100644 index 0000000..75a69ec --- /dev/null +++ b/generated/prod/docs/http_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
218resource_cpuintegerminvalue=100; maxvalue=4000
219resource_memoryintegerminvalue=128; maxvalue=8192
220resource_instancesintegerКоличество узловminvalue=1; maxvalue=7
diff --git a/generated/prod/docs/k8s_sthutrval_cluster.md b/generated/prod/docs/k8s_sthutrval_cluster.md new file mode 100644 index 0000000..b3b0523 --- /dev/null +++ b/generated/prod/docs/k8s_sthutrval_cluster.md @@ -0,0 +1,13 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## MAN + +
+Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле +
diff --git a/generated/prod/docs/k8s_sthutrval_cluster_example.md b/generated/prod/docs/k8s_sthutrval_cluster_example.md new file mode 100644 index 0000000..2b8b2ea --- /dev/null +++ b/generated/prod/docs/k8s_sthutrval_cluster_example.md @@ -0,0 +1,58 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Copy-ready manifest (`k8s_sthutrval_cluster_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_k8s_sthutrval_cluster" "baza" { + nsxt_uid = "TODO" # InstanceUid nsxt. Nsxt должен быть создан + control_plane_sizing_policy = "TODO" # Настройка Control Plane. CPU (шт) / MEM (gb). Наверное, должна подтягиваться из свойств ресурсной платформы + workers_sizing_policy = "TODO" # Настройка workers. CPU (шт) / MEM (gb) + vdc_uid = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + cluster_name = "example1" # Наименование кластера Kubernetes. Имя будет использоваться в fqdn (svc.$CLUSTER_NAME) + control_plane_sizing_disk = 30 # Настройка Control Plane. Disk (Gb) + control_plane_count = 1 + workers_sizing_disk = 40 # Настройка workers. Disk (Gb) + workers_count = 2 # Настройка workers. Кол-во нод + app_version = 2.12.1 # Версия Штурвал (не является версией Kubernetes) + need_external_address_api = true # Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false + access_ip_list_api = [] # Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем + need_external_address_ingress = true # Выделяет внешний IP для доступа к Ingress + access_ip_list_ingress = [] # Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_k8s_sthutrval_cluster.baza.state_params["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_out["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_params_flat["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_out_flat["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.vault_secrets["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.vault_url +nubes_k8s_sthutrval_cluster.baza.vault_user_path +nubes_k8s_sthutrval_cluster.baza.vault_fields +``` diff --git a/generated/prod/docs/k8s_sthutrval_cluster_ops.md b/generated/prod/docs/k8s_sthutrval_cluster_ops.md new file mode 100644 index 0000000..afb94fd --- /dev/null +++ b/generated/prod/docs/k8s_sthutrval_cluster_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](k8s_sthutrval_cluster_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](k8s_sthutrval_cluster_params_modify.md) +- `reconcile` +- `resume` +- `suspend` +- `update_secrets` diff --git a/generated/prod/docs/k8s_sthutrval_cluster_outputs.md b/generated/prod/docs/k8s_sthutrval_cluster_outputs.md new file mode 100644 index 0000000..065be3b --- /dev/null +++ b/generated/prod/docs/k8s_sthutrval_cluster_outputs.md @@ -0,0 +1,33 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["ingressAddress"]` +- `state_out_flat["kubernetesApiAddress"]` + +### `vault_secrets` ключи + +- `vault_secrets["kubernetesAdminConfigB64"]` + diff --git a/generated/prod/docs/k8s_sthutrval_cluster_params.md b/generated/prod/docs/k8s_sthutrval_cluster_params.md new file mode 100644 index 0000000..641f838 --- /dev/null +++ b/generated/prod/docs/k8s_sthutrval_cluster_params.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Parameters + +- [Create params](k8s_sthutrval_cluster_params_create.md) +- [Modify params](k8s_sthutrval_cluster_params_modify.md) diff --git a/generated/prod/docs/k8s_sthutrval_cluster_params_create.md b/generated/prod/docs/k8s_sthutrval_cluster_params_create.md new file mode 100644 index 0000000..6e21a3c --- /dev/null +++ b/generated/prod/docs/k8s_sthutrval_cluster_params_create.md @@ -0,0 +1,48 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
344nsxt_uiduuidInstanceUid nsxt. Nsxt должен быть созданref_svc_id=22
346control_plane_sizing_policystringНастройка Control Plane. CPU (шт) / MEM (gb).
Наверное, должна подтягиваться из свойств ресурсной платформы
349workers_sizing_policystringНастройка workers. CPU (шт) / MEM (gb)
747vdc_uiduuidref_svc_id=21
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
345cluster_namestringexample1Наименование кластера Kubernetes. Имя будет использоваться в fqdn (svc.$CLUSTER_NAME)regex=(?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$; unique_scope=provider
347control_plane_sizing_diskinteger30Настройка Control Plane. Disk (Gb)minvalue=20; maxvalue=999
348control_plane_countinteger1minvalue=1; maxvalue=9
350workers_sizing_diskinteger40Настройка workers. Disk (Gb)minvalue=20; maxvalue=999
351workers_countinteger2Настройка workers. Кол-во нодminvalue=1; maxvalue=99
352app_versionstring2.12.1Версия Штурвал (не является версией Kubernetes)value_list=2.12.1, 2.11.0
353need_external_address_apibooleantrueВыделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить falsevalue_list=true
354access_ip_list_apijson[]Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
355need_external_address_ingressbooleantrueВыделяет внешний IP для доступа к Ingressvalue_list=true
356access_ip_list_ingressjson[]Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/k8s_sthutrval_cluster_params_modify.md b/generated/prod/docs/k8s_sthutrval_cluster_params_modify.md new file mode 100644 index 0000000..21776a5 --- /dev/null +++ b/generated/prod/docs/k8s_sthutrval_cluster_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
362control_plane_countintegerКол-во нод Control Planeminvalue=1; maxvalue=9
363workers_countintegerКол-во нод Workersminvalue=1; maxvalue=99
364access_ip_list_apijsonAccess IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
365access_ip_list_ingressjsonAccess IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
563app_versionstringВерсия Штурвал (не является версией Kubernetes)
Понижать версию нельзя
Обновиться на несколько минорных нельзя
Невозможно перепрыгнуть не с последней минорной на мажорную
Можно обновиться на несколько патчей
value_list=2.11.0, 2.12.1
637control_plane_sizing_diskinteger
638workers_sizing_diskinteger
diff --git a/generated/prod/docs/k8s_ziti_controller.md b/generated/prod/docs/k8s_ziti_controller.md new file mode 100644 index 0000000..5c8b8f3 --- /dev/null +++ b/generated/prod/docs/k8s_ziti_controller.md @@ -0,0 +1,13 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/k8s_ziti_controller_example.md b/generated/prod/docs/k8s_ziti_controller_example.md new file mode 100644 index 0000000..7643188 --- /dev/null +++ b/generated/prod/docs/k8s_ziti_controller_example.md @@ -0,0 +1,50 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Copy-ready manifest (`k8s_ziti_controller_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_k8s_ziti_controller" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 300 # Квота ядра пода (milicores) + resource_memory = 512 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_k8s_ziti_controller.baza.state_params["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_out["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_params_flat["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_out_flat["имя_ключа"] +nubes_k8s_ziti_controller.baza.vault_secrets["имя_ключа"] +nubes_k8s_ziti_controller.baza.vault_url +nubes_k8s_ziti_controller.baza.vault_user_path +nubes_k8s_ziti_controller.baza.vault_fields +``` diff --git a/generated/prod/docs/k8s_ziti_controller_ops.md b/generated/prod/docs/k8s_ziti_controller_ops.md new file mode 100644 index 0000000..8da7234 --- /dev/null +++ b/generated/prod/docs/k8s_ziti_controller_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](k8s_ziti_controller_params_create.md) +- `delete` diff --git a/generated/prod/docs/k8s_ziti_controller_outputs.md b/generated/prod/docs/k8s_ziti_controller_outputs.md new file mode 100644 index 0000000..6fc1fdd --- /dev/null +++ b/generated/prod/docs/k8s_ziti_controller_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/k8s_ziti_controller_params.md b/generated/prod/docs/k8s_ziti_controller_params.md new file mode 100644 index 0000000..343d93e --- /dev/null +++ b/generated/prod/docs/k8s_ziti_controller_params.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Parameters + +- [Create params](k8s_ziti_controller_params_create.md) +- [Modify params](k8s_ziti_controller_params_modify.md) diff --git a/generated/prod/docs/k8s_ziti_controller_params_create.md b/generated/prod/docs/k8s_ziti_controller_params_create.md new file mode 100644 index 0000000..dac54da --- /dev/null +++ b/generated/prod/docs/k8s_ziti_controller_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
298resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
294resource_cpuinteger300Квота ядра пода (milicores)minvalue=100; maxvalue=4000
295resource_memoryinteger512Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
296resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
297resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
299domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
diff --git a/generated/prod/docs/k8s_ziti_controller_params_modify.md b/generated/prod/docs/k8s_ziti_controller_params_modify.md new file mode 100644 index 0000000..1998e6a --- /dev/null +++ b/generated/prod/docs/k8s_ziti_controller_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/kafka.md b/generated/prod/docs/kafka.md new file mode 100644 index 0000000..1c4e5e5 --- /dev/null +++ b/generated/prod/docs/kafka.md @@ -0,0 +1,13 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## MAN + +
+# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
Брокер сообщений Apache Kafka — это отказоустойчивая распределённая платформа для обмена сообщениями и потоковой обработки данных в реальном времени.
Позволяет надёжно передавать события между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: ` k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов (брокеров) в кластере Kafka

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**internalConnect**
Это внутренний эндпоинт, который используется для подключения к кластеру Kafka из других сервисов, развернутых внутри той же сети.
Эндпоинт предоставляет доступ к брокерам Kafka по внутренним портам, указанным в конфигурации слушателей.

**externalConnect**
Это внешний эндпоинт (fqdn), который используется для подключения к кластеру Kafka. Для подключения используеться 9093 порт.

Для подключения используются следующие порты:

* 9092 — внутренний нешифрованный доступ (plain).
* 9093 — внутренний и внешний доступ по TLS, с обязательной TLS-аутентификацией.

---
## 5. Дополнительная информация
Для создания топиков, пользователей и управления ими, а также для интеграции с кластерами Kafka используйте соответствующие сервисы и раздел "Облачные сервисы".
--- +
diff --git a/generated/prod/docs/kafka_example.md b/generated/prod/docs/kafka_example.md new file mode 100644 index 0000000..ec23b8d --- /dev/null +++ b/generated/prod/docs/kafka_example.md @@ -0,0 +1,50 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Copy-ready manifest (`kafka_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_kafka" "baza" { + resource_instances = 1 # Пример: `2` + resource_memory = 1 # Пример: `2048` + resource_cpu = 1 # Пример: `2000` + resource_disk = 1 # Пример: `20` + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_kafka.baza.state_params["имя_ключа"] +nubes_kafka.baza.state_out["имя_ключа"] +nubes_kafka.baza.state_params_flat["имя_ключа"] +nubes_kafka.baza.state_out_flat["имя_ключа"] +nubes_kafka.baza.vault_secrets["имя_ключа"] +nubes_kafka.baza.vault_url +nubes_kafka.baza.vault_user_path +nubes_kafka.baza.vault_fields +``` diff --git a/generated/prod/docs/kafka_ops.md b/generated/prod/docs/kafka_ops.md new file mode 100644 index 0000000..21e6e30 --- /dev/null +++ b/generated/prod/docs/kafka_ops.md @@ -0,0 +1,21 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](kafka_params_create.md) +- `create_topic` — Создание кластера. Параметры: [Create params](kafka_params_create.md) +- `create_user` — Создание кластера. Параметры: [Create params](kafka_params_create.md) +- `delete` +- `delete_topic` +- `delete_user` — # Инструкция по управлению пользователями Apache Kafka. --- ## 1. Общая информация Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру. Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам. Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka. --- ## Доступные операции - **create_user** — Создание пользователя - **modify_user** — Модификация пользователя - **delete_user** — Удаление пользователя --- ## 2. Параметры создания пользователя Ниже перечислены параметры, которые необходимо указать при создании нового пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 3. Параметры модификации пользователя Ниже перечислены параметры, которые необходимо указать при модификации пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 4. Параметры удалении пользователя Ниже перечислены параметры, которые необходимо указать при удалении пользователя: * **Имя пользователя** Имя удаляемого пользователя. Пример: `username-admin` ## 5. Выходные параметры После успешного создания пользователя платформа возвращает параметры, необходимые для подключения: * **ca.crt** Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении. Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **ca.p12** PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.p12 ``` * **ca.password** Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно. * **user.crt** Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka. Используется совместно с приватным ключом `user.key` для аутентификации. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **user.key** Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.key ``` * **user.p12** PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo "<содержание поля>" | base64 -d > <название>.p12 ``` * **user.password** Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно. Эти параметры используются для настройки TLS-подключения. --- ## 6. Пример конфигурации клиента внутри кластера Пример пода: ```yaml apiVersion: v1 kind: Pod metadata: name: kafka-4 spec: volumes: - name: work emptyDir: {} containers: - name: kcat image: quay.io/strimzi/kafka:latest-kafka-4.0.0 command: ["sleep", "5600"] volumeMounts: - name: work mountPath: /work restartPolicy: Never ``` Создаём под и сохраняем сертификаты: ```bash kubectl cp user.p12 kafka-4:/work/ kubectl cp ca.p12 kafka-4:/work/ ``` Создаём файлы конфигурации: ```bash cat > /work/user-read.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` ```bash cat > /work/user-write.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user-write.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` Листинг доступных топиков: ```bash /opt/kafka/bin/kafka-topics.sh \ --bootstrap-server kafkak8s-bootstrap-external:9093 \ --command-config /work/user-read.properties \ --list ``` Запись сообщений: ```bash /opt/kafka/bin/kafka-console-producer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --producer.config /work/user-write.properties ``` Чтение сообщений: ```bash /opt/kafka/bin/kafka-console-consumer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --from-beginning \ --group \ --consumer.config /work/user-read.properties \ --max-messages 50 ``` --- ## 7. Пример внешнего подключения с использованием **kcat** **Листинг топиков** ```bash kcat -b :9093 \ -L \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` **Запись сообщения** ```bash echo "hello from kcat" | kcat -P \ -b :9093 \ -t write-test \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-write.crt \ -X ssl.key.location=./user-write.key \ -X ssl.endpoint.identification.algorithm=none ``` **Чтение сообщений** ```bash kcat -C \ -b :9093 \ -t write-test \ -o beginning \ -q \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` --- ## 9. Рекомендации * Минимизируйте набор разрешённых операций для повышения безопасности. * Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов. * Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**. * После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам. --- +- `modify` — Изменение кластера. Параметры: [Modify params](kafka_params_modify.md) +- `modify_topic` — Изменение кластера. Параметры: [Modify params](kafka_params_modify.md) +- `modify_user` — Изменение кластера. Параметры: [Modify params](kafka_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/kafka_outputs.md b/generated/prod/docs/kafka_outputs.md new file mode 100644 index 0000000..7a36056 --- /dev/null +++ b/generated/prod/docs/kafka_outputs.md @@ -0,0 +1,54 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.bootstrap"]` +- `state_out_flat["externalConnect.bootstrap.fqdn"]` +- `state_out_flat["externalConnect.bootstrap.ip"]` +- `state_out_flat["externalConnect.bootstrap.isExternal"]` +- `state_out_flat["externalConnect.bootstrap.uuid"]` +- `state_out_flat["internalConnect"]` +- `state_out_flat["kafkaTopics"]` +- `state_out_flat["kafkaTopics.test-events"]` +- `state_out_flat["kafkaTopics.test-events.partitions"]` +- `state_out_flat["kafkaTopics.test-events.replicas"]` +- `state_out_flat["kafkaUsers"]` +- `state_out_flat["kafkaUsers.usertest"]` +- `state_out_flat["kafkaUsers.usertest.accessHosts"]` +- `state_out_flat["kafkaUsers.usertest.group"]` +- `state_out_flat["kafkaUsers.usertest.nameTopic"]` +- `state_out_flat["kafkaUsers.usertest.operations"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["users"]` + diff --git a/generated/prod/docs/kafka_params.md b/generated/prod/docs/kafka_params.md new file mode 100644 index 0000000..25cb573 --- /dev/null +++ b/generated/prod/docs/kafka_params.md @@ -0,0 +1,12 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Parameters + +- [Create params](kafka_params_create.md) +- [Modify params](kafka_params_modify.md) diff --git a/generated/prod/docs/kafka_params_create.md b/generated/prod/docs/kafka_params_create.md new file mode 100644 index 0000000..1a43a4e --- /dev/null +++ b/generated/prod/docs/kafka_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
461resource_instancesintegerПример: `2`minvalue=1; value_list=1, 2, 3, 4, 5
462resource_memoryintegerПример: `2048`minvalue=1000
463resource_cpuintegerПример: `2000`minvalue=1000
464resource_diskintegerПример: `20`
466ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
469resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
465need_external_address_masterbooleanfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`value_list=false, true
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/kafka_params_modify.md b/generated/prod/docs/kafka_params_modify.md new file mode 100644 index 0000000..e6ff431 --- /dev/null +++ b/generated/prod/docs/kafka_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
501resource_instancesintegerПример: `2`value_list=1, 2, 3, 4, 5
502resource_memoryintegerПример: `2048`minvalue=1000
503resource_cpuПример: `2000`minvalue=1000
504resource_diskintegerПример: `20`
505need_external_address_masterbooleanФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`value_list=false, true
506ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
diff --git a/generated/prod/docs/lucee.md b/generated/prod/docs/lucee.md new file mode 100644 index 0000000..ca3542d --- /dev/null +++ b/generated/prod/docs/lucee.md @@ -0,0 +1,13 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/lucee_example.md b/generated/prod/docs/lucee_example.md new file mode 100644 index 0000000..5f5ea22 --- /dev/null +++ b/generated/prod/docs/lucee_example.md @@ -0,0 +1,52 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Copy-ready manifest (`lucee_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_lucee" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + git_path = "https://github.com/xahys/testlucee" # Путь до Git, который будет webroot для сайта + json_env = { "param": "value" } # Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения. + resource_cpu = 300 # Квота ядра пода (milicores) + resource_memory = 512 # Квота памяти пода (MBytes) + resource_instances = 1 # Количество узлов + app_version = 5.4 # Версия Lucee +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_lucee.baza.state_params["имя_ключа"] +nubes_lucee.baza.state_out["имя_ключа"] +nubes_lucee.baza.state_params_flat["имя_ключа"] +nubes_lucee.baza.state_out_flat["имя_ключа"] +nubes_lucee.baza.vault_secrets["имя_ключа"] +nubes_lucee.baza.vault_url +nubes_lucee.baza.vault_user_path +nubes_lucee.baza.vault_fields +``` diff --git a/generated/prod/docs/lucee_ops.md b/generated/prod/docs/lucee_ops.md new file mode 100644 index 0000000..3294ee8 --- /dev/null +++ b/generated/prod/docs/lucee_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](lucee_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](lucee_params_modify.md) +- `redeploy` +- `restart` +- `resume` +- `suspend` diff --git a/generated/prod/docs/lucee_outputs.md b/generated/prod/docs/lucee_outputs.md new file mode 100644 index 0000000..838f21d --- /dev/null +++ b/generated/prod/docs/lucee_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/prod/docs/lucee_params.md b/generated/prod/docs/lucee_params.md new file mode 100644 index 0000000..730e3b2 --- /dev/null +++ b/generated/prod/docs/lucee_params.md @@ -0,0 +1,12 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Parameters + +- [Create params](lucee_params_create.md) +- [Modify params](lucee_params_modify.md) diff --git a/generated/prod/docs/lucee_params_create.md b/generated/prod/docs/lucee_params_create.md new file mode 100644 index 0000000..f63e87a --- /dev/null +++ b/generated/prod/docs/lucee_params_create.md @@ -0,0 +1,43 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
108resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
132health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
103domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
104git_pathstringhttps://github.com/xahys/testluceeПуть до Git, который будет webroot для сайта
105json_envjson{ "param": "value" }Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения.maxlength=2048
106resource_cpuinteger300Квота ядра пода (milicores)minvalue=100; maxvalue=4000
107resource_memoryinteger512Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
139resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
263app_versionstring5.4Версия Luceeregex=^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$; value_list=5.4, 6.0
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/lucee_params_modify.md b/generated/prod/docs/lucee_params_modify.md new file mode 100644 index 0000000..087ab68 --- /dev/null +++ b/generated/prod/docs/lucee_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
133git_pathstringПуть до Git, который будет webroot для сайта
134health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код.
135json_envjsonМассив переменных, будут переданы в env приложенияmaxlength=2048
136resource_cpuintegerКвота ядра пода (Указывается в mili)minvalue=100; maxvalue=4000
137resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
138resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
264app_versionstringregex=^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$; value_list=5.4, 6.0
diff --git a/generated/prod/docs/mariadb.md b/generated/prod/docs/mariadb.md new file mode 100644 index 0000000..9161094 --- /dev/null +++ b/generated/prod/docs/mariadb.md @@ -0,0 +1,13 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## MAN + +
+Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление. +
diff --git a/generated/prod/docs/mariadb_example.md b/generated/prod/docs/mariadb_example.md new file mode 100644 index 0000000..f5b8378 --- /dev/null +++ b/generated/prod/docs/mariadb_example.md @@ -0,0 +1,57 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Copy-ready manifest (`mariadb_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_mariadb" "baza" { + resource_realm = "TODO" # Платформа для развертывания + s3_uid = "TODO" # Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (milicores) + resource_memory = 1024 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + need_external_address_master = false # Выделяет внешний (белый) IP для доступа к master + ext_backup_schedule = "0 * * * *" # Не обязательный. Время запуска бекапа + app_version = 9.4.0 # Версия MySQL + auto_scale = false # Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + auto_scale_percentage = 10 # Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + auto_scale_tech_window = 0 # Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + auto_scale_quota_gb = 1 # Квота для autoScale +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_mariadb.baza.state_params["имя_ключа"] +nubes_mariadb.baza.state_out["имя_ключа"] +nubes_mariadb.baza.state_params_flat["имя_ключа"] +nubes_mariadb.baza.state_out_flat["имя_ключа"] +nubes_mariadb.baza.vault_secrets["имя_ключа"] +nubes_mariadb.baza.vault_url +nubes_mariadb.baza.vault_user_path +nubes_mariadb.baza.vault_fields +``` diff --git a/generated/prod/docs/mariadb_ops.md b/generated/prod/docs/mariadb_ops.md new file mode 100644 index 0000000..32042ca --- /dev/null +++ b/generated/prod/docs/mariadb_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](mariadb_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](mariadb_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/mariadb_outputs.md b/generated/prod/docs/mariadb_outputs.md new file mode 100644 index 0000000..38f0606 --- /dev/null +++ b/generated/prod/docs/mariadb_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/mariadb_params.md b/generated/prod/docs/mariadb_params.md new file mode 100644 index 0000000..1d0477b --- /dev/null +++ b/generated/prod/docs/mariadb_params.md @@ -0,0 +1,12 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Parameters + +- [Create params](mariadb_params_create.md) +- [Modify params](mariadb_params_modify.md) diff --git a/generated/prod/docs/mariadb_params_create.md b/generated/prod/docs/mariadb_params_create.md new file mode 100644 index 0000000..4f6f74c --- /dev/null +++ b/generated/prod/docs/mariadb_params_create.md @@ -0,0 +1,48 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
422resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
429ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster
459s3_uiduuidЭкземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательноref_svc_id=12
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
424resource_cpuinteger500Квота ядра пода (milicores)minvalue=500; maxvalue=4000
425resource_memoryinteger1024Квота памяти пода (MBytes)minvalue=1024; maxvalue=8192
426resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
427resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
428need_external_address_masterbooleanfalseВыделяет внешний (белый) IP для доступа к mastervalue_list=false, true
430ext_backup_schedulestring0 * * * *Не обязательный. Время запуска бекапаregex=^(\*|(\d+|\*\/\d+|\d+(,\d+)*))\s\*\s\*\s\*\s\*$
431app_versionstring9.4.0Версия MySQLvalue_list=8.4.0, 9.4.0
432auto_scalebooleanfalseВключает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услугиvalue_list=false, true
433auto_scale_percentageinteger10Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объемаvalue_list=10, 15, 20
434auto_scale_tech_windowinteger0Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)value_list=0, 5, 12, 23
435auto_scale_quota_gbinteger1Квота для autoScale
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/mariadb_params_modify.md b/generated/prod/docs/mariadb_params_modify.md new file mode 100644 index 0000000..c17f0d8 --- /dev/null +++ b/generated/prod/docs/mariadb_params_modify.md @@ -0,0 +1,25 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Modify params + + + + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
436resource_cpuinteger
437resource_memoryinteger
438resource_instancesinteger
439resource_diskinteger
440need_external_address_masterbooleanvalue_list=false, true
441ip_space_name_master
442ext_backup_schedulestring
443auto_scalebooleanvalue_list=false, true
444auto_scale_percentageinteger
445auto_scale_tech_windowinteger
446auto_scale_quota_gbinteger
diff --git a/generated/prod/docs/mongodb.md b/generated/prod/docs/mongodb.md new file mode 100644 index 0000000..fb0375a --- /dev/null +++ b/generated/prod/docs/mongodb.md @@ -0,0 +1,13 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## MAN + +
+# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание кластера
* **delete** — удаление кластера
* **suspend** — остановка кластера
* **resume** — запуск кластера

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания кластера

Эти параметры необходимо указать при создании кластера.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Оптимально выбирать нечётное количество узлов в кластере MongoDB, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность кластера даже при отказе одной из нод.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Выделение внешнего IP для доступа к master

Флаг, определяющий необходимость выделения внешнего IP.
Допустимые значения: `true` или `false`

### Имя ipSpace для публикации внешнего IP

Указывается только если включено выделение внешнего IP.
Пример: `internet-no-antiddos-v1`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания кластера платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю. +
diff --git a/generated/prod/docs/mongodb_example.md b/generated/prod/docs/mongodb_example.md new file mode 100644 index 0000000..c799e4f --- /dev/null +++ b/generated/prod/docs/mongodb_example.md @@ -0,0 +1,50 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Copy-ready manifest (`mongodb_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_mongodb" "baza" { + resource_instances = 1 # Пример: `2` + resource_memory = 1 # Пример: `2048` + resource_cpu = 1 # Пример: `1000` + resource_disk = 1 # Пример: `20` + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_mongodb.baza.state_params["имя_ключа"] +nubes_mongodb.baza.state_out["имя_ключа"] +nubes_mongodb.baza.state_params_flat["имя_ключа"] +nubes_mongodb.baza.state_out_flat["имя_ключа"] +nubes_mongodb.baza.vault_secrets["имя_ключа"] +nubes_mongodb.baza.vault_url +nubes_mongodb.baza.vault_user_path +nubes_mongodb.baza.vault_fields +``` diff --git a/generated/prod/docs/mongodb_ops.md b/generated/prod/docs/mongodb_ops.md new file mode 100644 index 0000000..b933306 --- /dev/null +++ b/generated/prod/docs/mongodb_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](mongodb_params_create.md) +- `create_user` — Создание кластера. Параметры: [Create params](mongodb_params_create.md) +- `delete` +- `delete_user` +- `modify_user` — Изменение кластера. Параметры: [Modify params](mongodb_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/mongodb_outputs.md b/generated/prod/docs/mongodb_outputs.md new file mode 100644 index 0000000..2a6b2dc --- /dev/null +++ b/generated/prod/docs/mongodb_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["internalConnect"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/prod/docs/mongodb_params.md b/generated/prod/docs/mongodb_params.md new file mode 100644 index 0000000..b595ec1 --- /dev/null +++ b/generated/prod/docs/mongodb_params.md @@ -0,0 +1,12 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Parameters + +- [Create params](mongodb_params_create.md) +- [Modify params](mongodb_params_modify.md) diff --git a/generated/prod/docs/mongodb_params_create.md b/generated/prod/docs/mongodb_params_create.md new file mode 100644 index 0000000..ce9ff39 --- /dev/null +++ b/generated/prod/docs/mongodb_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
487resource_instancesintegerПример: `2`minvalue=1; maxvalue=7
588resource_memoryintegerПример: `2048`minvalue=1000
589resource_cpuintegerПример: `1000`
590resource_diskintegerПример: `20`
595resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
646ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
645need_external_address_masterstringfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`value_list=false, true
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/mongodb_params_modify.md b/generated/prod/docs/mongodb_params_modify.md new file mode 100644 index 0000000..369e9ef --- /dev/null +++ b/generated/prod/docs/mongodb_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/nextcloud.md b/generated/prod/docs/nextcloud.md new file mode 100644 index 0000000..a494623 --- /dev/null +++ b/generated/prod/docs/nextcloud.md @@ -0,0 +1,13 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## MAN + +
+Ресурсная платформа: k8s. Автоматизация: helm chart +
diff --git a/generated/prod/docs/nextcloud_example.md b/generated/prod/docs/nextcloud_example.md new file mode 100644 index 0000000..50ada44 --- /dev/null +++ b/generated/prod/docs/nextcloud_example.md @@ -0,0 +1,53 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Copy-ready manifest (`nextcloud_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nextcloud" "baza" { + psql_uid = "TODO" + redis_uid = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 1000 # Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili + resource_memory = 1024 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 2 # Квота диска (Указывается в GIGAbytes) Диск не может быть меньше 2G (!) + document_server = "collabora" # Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен Размер пода collabora: 1.8 ядра. 2Gi ОЗУ + resource_instances = 1 # Количество узлов Не может пока быть больше одного, так как нужны SC под RWMany + app_version = 32.0.1 +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nextcloud.baza.state_params["имя_ключа"] +nubes_nextcloud.baza.state_out["имя_ключа"] +nubes_nextcloud.baza.state_params_flat["имя_ключа"] +nubes_nextcloud.baza.state_out_flat["имя_ключа"] +nubes_nextcloud.baza.vault_secrets["имя_ключа"] +nubes_nextcloud.baza.vault_url +nubes_nextcloud.baza.vault_user_path +nubes_nextcloud.baza.vault_fields +``` diff --git a/generated/prod/docs/nextcloud_ops.md b/generated/prod/docs/nextcloud_ops.md new file mode 100644 index 0000000..2dd5b5f --- /dev/null +++ b/generated/prod/docs/nextcloud_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nextcloud_params_create.md) +- `delete` — DEV-Ветка удаления Nextcloud +- `modify` — Изменение кластера. Параметры: [Modify params](nextcloud_params_modify.md) +- `restart` +- `resume` +- `suspend` diff --git a/generated/prod/docs/nextcloud_outputs.md b/generated/prod/docs/nextcloud_outputs.md new file mode 100644 index 0000000..1a8707b --- /dev/null +++ b/generated/prod/docs/nextcloud_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/nextcloud_params.md b/generated/prod/docs/nextcloud_params.md new file mode 100644 index 0000000..2c1eb71 --- /dev/null +++ b/generated/prod/docs/nextcloud_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Parameters + +- [Create params](nextcloud_params_create.md) +- [Modify params](nextcloud_params_modify.md) diff --git a/generated/prod/docs/nextcloud_params_create.md b/generated/prod/docs/nextcloud_params_create.md new file mode 100644 index 0000000..b3e6a19 --- /dev/null +++ b/generated/prod/docs/nextcloud_params_create.md @@ -0,0 +1,43 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
564psql_uiduuidref_svc_id=90
570redis_uiduuidref_svc_id=91
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
21domainstringweb01Наименование домена. Если будет указан FQDN, то происходят проверки DNS. Если указан поддомен, то будет добавлен в зону services.ngcloud.ru
Можно указать клиентскую DNS-Запись. Важно, чтобы запись смотрела на Ingress кластера
regex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
99resource_cpuinteger1000Квота ядра пода (Указывается в mili). 1 ядро = 1000 Miliminvalue=100; maxvalue=4000
100resource_memoryinteger1024Квота памяти пода (Указывается в Megabytes)minvalue=1024; maxvalue=8192
101resource_diskinteger2Квота диска (Указывается в GIGAbytes)minvalue=2; maxvalue=999
192document_serverstringcollaboraВыбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступенvalue_list=collabora
470resource_instancesinteger1Количество узлов
Не может пока быть больше одного, так как нужны SC под RWMany
minvalue=1; maxvalue=7; value_list=1
569app_version32.0.1value_list=32.0.1
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/nextcloud_params_modify.md b/generated/prod/docs/nextcloud_params_modify.md new file mode 100644 index 0000000..4da066d --- /dev/null +++ b/generated/prod/docs/nextcloud_params_modify.md @@ -0,0 +1,19 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Modify params + + + + + + + + + +
IDCodeTypeDescriptionConstraints
178resource_cpuintegerКвота ядра пода (Указывается в mili)minvalue=100; maxvalue=4000
179resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
180resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
547resource_instancesintegerКоличество узлов.
Не может пока быть больше одного, так как нужны SC под RWMany
minvalue=1; maxvalue=7; value_list=1
571app_versionstringНельзя обновляться сразу через несколько версий сразуvalue_list=32.0.1
diff --git a/generated/prod/docs/nifi.md b/generated/prod/docs/nifi.md new file mode 100644 index 0000000..cf2c590 --- /dev/null +++ b/generated/prod/docs/nifi.md @@ -0,0 +1,13 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/nifi_example.md b/generated/prod/docs/nifi_example.md new file mode 100644 index 0000000..5e78168 --- /dev/null +++ b/generated/prod/docs/nifi_example.md @@ -0,0 +1,46 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Copy-ready manifest (`nifi_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nifi" "baza" { + kafka_uid = "TODO" # Выбрать кластер из списка + partitions = 1 # Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` + replicas = 1 # Количество партиций ≤ (число брокеров). Пример `2` + name_topic = "TODO" # Пример: `orders-events` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nifi.baza.state_params["имя_ключа"] +nubes_nifi.baza.state_out["имя_ключа"] +nubes_nifi.baza.state_params_flat["имя_ключа"] +nubes_nifi.baza.state_out_flat["имя_ключа"] +nubes_nifi.baza.vault_secrets["имя_ключа"] +nubes_nifi.baza.vault_url +nubes_nifi.baza.vault_user_path +nubes_nifi.baza.vault_fields +``` diff --git a/generated/prod/docs/nifi_ops.md b/generated/prod/docs/nifi_ops.md new file mode 100644 index 0000000..a896c6a --- /dev/null +++ b/generated/prod/docs/nifi_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nifi_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](nifi_params_modify.md) diff --git a/generated/prod/docs/nifi_outputs.md b/generated/prod/docs/nifi_outputs.md new file mode 100644 index 0000000..976b25e --- /dev/null +++ b/generated/prod/docs/nifi_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/nifi_params.md b/generated/prod/docs/nifi_params.md new file mode 100644 index 0000000..4dcadac --- /dev/null +++ b/generated/prod/docs/nifi_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Parameters + +- [Create params](nifi_params_create.md) +- [Modify params](nifi_params_modify.md) diff --git a/generated/prod/docs/nifi_params_create.md b/generated/prod/docs/nifi_params_create.md new file mode 100644 index 0000000..d5c4baa --- /dev/null +++ b/generated/prod/docs/nifi_params_create.md @@ -0,0 +1,23 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
471kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
472partitionsintegerОбратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.
473replicasintegerКоличество партиций ≤ (число брокеров). Пример `2`
474name_topicstringПример: `orders-events`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/prod/docs/nifi_params_modify.md b/generated/prod/docs/nifi_params_modify.md new file mode 100644 index 0000000..7f34ef0 --- /dev/null +++ b/generated/prod/docs/nifi_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
477partitionsintegerОбратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.
478replicasintegerКоличество партиций ≤ (число брокеров). Пример `2`
diff --git a/generated/prod/docs/nodejs.md b/generated/prod/docs/nodejs.md new file mode 100644 index 0000000..020cc2f --- /dev/null +++ b/generated/prod/docs/nodejs.md @@ -0,0 +1,13 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## MAN + +
+Развертывает контейнер в k8s кластере с node:16, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. +
diff --git a/generated/prod/docs/nodejs_example.md b/generated/prod/docs/nodejs_example.md new file mode 100644 index 0000000..2e77f32 --- /dev/null +++ b/generated/prod/docs/nodejs_example.md @@ -0,0 +1,51 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Copy-ready manifest (`nodejs_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nodejs" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + git_path = "https://github.com/xahys/testnode.git" # Путь до Git, который будет webroot для сайта + resource_cpu = 500 # Квота ядра пода (milicores). 1000Mili = 1 Ядро + resource_memory = 1024 # Квота памяти пода (MBytes) + resource_instances = 1 # Количество узлов + app_version = 23 # Версия приложения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nodejs.baza.state_params["имя_ключа"] +nubes_nodejs.baza.state_out["имя_ключа"] +nubes_nodejs.baza.state_params_flat["имя_ключа"] +nubes_nodejs.baza.state_out_flat["имя_ключа"] +nubes_nodejs.baza.vault_secrets["имя_ключа"] +nubes_nodejs.baza.vault_url +nubes_nodejs.baza.vault_user_path +nubes_nodejs.baza.vault_fields +``` diff --git a/generated/prod/docs/nodejs_ops.md b/generated/prod/docs/nodejs_ops.md new file mode 100644 index 0000000..de1e172 --- /dev/null +++ b/generated/prod/docs/nodejs_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nodejs_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](nodejs_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/prod/docs/nodejs_outputs.md b/generated/prod/docs/nodejs_outputs.md new file mode 100644 index 0000000..5fa8a14 --- /dev/null +++ b/generated/prod/docs/nodejs_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/prod/docs/nodejs_params.md b/generated/prod/docs/nodejs_params.md new file mode 100644 index 0000000..daf96ca --- /dev/null +++ b/generated/prod/docs/nodejs_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Parameters + +- [Create params](nodejs_params_create.md) +- [Modify params](nodejs_params_modify.md) diff --git a/generated/prod/docs/nodejs_params_create.md b/generated/prod/docs/nodejs_params_create.md new file mode 100644 index 0000000..1bf4766 --- /dev/null +++ b/generated/prod/docs/nodejs_params_create.md @@ -0,0 +1,43 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
151health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет
152json_envjsonМассив переменных, будут переданы в env приложения
156resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
149domainstringweb01Наименование поддомена, будет добавлено в nodejs.dev.nubes.ruregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
150git_pathstringhttps://github.com/xahys/testnode.gitПуть до Git, который будет webroot для сайта
153resource_cpuinteger500Квота ядра пода (milicores). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
154resource_memoryinteger1024Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
155resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
270app_versionstring23Версия приложенияregex=^(\d){2}$; value_list=18, 20, 22, 23
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/nodejs_params_modify.md b/generated/prod/docs/nodejs_params_modify.md new file mode 100644 index 0000000..89a753e --- /dev/null +++ b/generated/prod/docs/nodejs_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
157git_pathstringПуть до Git, который будет webroot для сайта
158health_pathstringПуть до healtcheck для запуска пода в Kubernetes, который отдаст 200 код.
159json_envjsonМассив переменных, будут переданы в env приложенияmaxlength=2048
160resource_cpuintegerКвота ядра пода (Указывается в mili) 1000Mili = 1 ядроminvalue=100; maxvalue=4000
161resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
162resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
271app_versionstringВерсия приложенияregex=^(\d){2}$; value_list=18, 20, 22, 23
diff --git a/generated/prod/docs/nodered.md b/generated/prod/docs/nodered.md new file mode 100644 index 0000000..54ecff3 --- /dev/null +++ b/generated/prod/docs/nodered.md @@ -0,0 +1,13 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/nodered_example.md b/generated/prod/docs/nodered_example.md new file mode 100644 index 0000000..1ccf892 --- /dev/null +++ b/generated/prod/docs/nodered_example.md @@ -0,0 +1,50 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Copy-ready manifest (`nodered_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_nodered" "baza" { + resource_realm = "TODO" # Платформа для развертывания + domain = "TODO" # Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nodered.baza.state_params["имя_ключа"] +nubes_nodered.baza.state_out["имя_ключа"] +nubes_nodered.baza.state_params_flat["имя_ключа"] +nubes_nodered.baza.state_out_flat["имя_ключа"] +nubes_nodered.baza.vault_secrets["имя_ключа"] +nubes_nodered.baza.vault_url +nubes_nodered.baza.vault_user_path +nubes_nodered.baza.vault_fields +``` diff --git a/generated/prod/docs/nodered_ops.md b/generated/prod/docs/nodered_ops.md new file mode 100644 index 0000000..2d6074e --- /dev/null +++ b/generated/prod/docs/nodered_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nodered_params_create.md) +- `delete` diff --git a/generated/prod/docs/nodered_outputs.md b/generated/prod/docs/nodered_outputs.md new file mode 100644 index 0000000..74e4213 --- /dev/null +++ b/generated/prod/docs/nodered_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/nodered_params.md b/generated/prod/docs/nodered_params.md new file mode 100644 index 0000000..60a797d --- /dev/null +++ b/generated/prod/docs/nodered_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Parameters + +- [Create params](nodered_params_create.md) +- [Modify params](nodered_params_modify.md) diff --git a/generated/prod/docs/nodered_params_create.md b/generated/prod/docs/nodered_params_create.md new file mode 100644 index 0000000..f976ccb --- /dev/null +++ b/generated/prod/docs/nodered_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
196resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
197domainstringНаименование поддомена, будет добавлено в k8stemplate.dev.nubes.ruregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
193resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
194resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
195resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
323resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
diff --git a/generated/prod/docs/nodered_params_modify.md b/generated/prod/docs/nodered_params_modify.md new file mode 100644 index 0000000..be00f3b --- /dev/null +++ b/generated/prod/docs/nodered_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/openwhisk.md b/generated/prod/docs/openwhisk.md new file mode 100644 index 0000000..059ed46 --- /dev/null +++ b/generated/prod/docs/openwhisk.md @@ -0,0 +1,13 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## MAN + +
+Добавление и удаления пользователя для работы с Openwhisk +
diff --git a/generated/prod/docs/openwhisk_example.md b/generated/prod/docs/openwhisk_example.md new file mode 100644 index 0000000..d98bba9 --- /dev/null +++ b/generated/prod/docs/openwhisk_example.md @@ -0,0 +1,44 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Copy-ready manifest (`openwhisk_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_openwhisk" "baza" { + username = "TODO" # Имя пользователя Openwhisk + resource_realm = "TODO" # Ресурсная платформа +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_openwhisk.baza.state_params["имя_ключа"] +nubes_openwhisk.baza.state_out["имя_ключа"] +nubes_openwhisk.baza.state_params_flat["имя_ключа"] +nubes_openwhisk.baza.state_out_flat["имя_ключа"] +nubes_openwhisk.baza.vault_secrets["имя_ключа"] +nubes_openwhisk.baza.vault_url +nubes_openwhisk.baza.vault_user_path +nubes_openwhisk.baza.vault_fields +``` diff --git a/generated/prod/docs/openwhisk_ops.md b/generated/prod/docs/openwhisk_ops.md new file mode 100644 index 0000000..91fee4e --- /dev/null +++ b/generated/prod/docs/openwhisk_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](openwhisk_params_create.md) +- `delete` diff --git a/generated/prod/docs/openwhisk_outputs.md b/generated/prod/docs/openwhisk_outputs.md new file mode 100644 index 0000000..e2cdfe5 --- /dev/null +++ b/generated/prod/docs/openwhisk_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/openwhisk_params.md b/generated/prod/docs/openwhisk_params.md new file mode 100644 index 0000000..1a9b0e5 --- /dev/null +++ b/generated/prod/docs/openwhisk_params.md @@ -0,0 +1,12 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Parameters + +- [Create params](openwhisk_params_create.md) +- [Modify params](openwhisk_params_modify.md) diff --git a/generated/prod/docs/openwhisk_params_create.md b/generated/prod/docs/openwhisk_params_create.md new file mode 100644 index 0000000..e933788 --- /dev/null +++ b/generated/prod/docs/openwhisk_params_create.md @@ -0,0 +1,21 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
64usernamestringИмя пользователя Openwhisk
863resource_realmstringРесурсная платформаfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/prod/docs/openwhisk_params_modify.md b/generated/prod/docs/openwhisk_params_modify.md new file mode 100644 index 0000000..7791043 --- /dev/null +++ b/generated/prod/docs/openwhisk_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/pgadmin.md b/generated/prod/docs/pgadmin.md new file mode 100644 index 0000000..5c2feaf --- /dev/null +++ b/generated/prod/docs/pgadmin.md @@ -0,0 +1,13 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## MAN + +
+Инструмент с графическим интерфейсом для управления базами данных PostgreSQL +
diff --git a/generated/prod/docs/pgadmin_example.md b/generated/prod/docs/pgadmin_example.md new file mode 100644 index 0000000..978be3b --- /dev/null +++ b/generated/prod/docs/pgadmin_example.md @@ -0,0 +1,51 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Copy-ready manifest (`pgadmin_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_pgadmin" "baza" { + resource_realm = "TODO" # Платформа для развертывания + login = "TODO" # Логин для подключения к pgAdmin, в формате email (login@email.net) + password = "TODO" # Пароль для подключения к pgAdmin + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 200 # Квота ядра пода (milicores). 1000Mili = 1 Ядро + resource_memory = 256 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_pgadmin.baza.state_params["имя_ключа"] +nubes_pgadmin.baza.state_out["имя_ключа"] +nubes_pgadmin.baza.state_params_flat["имя_ключа"] +nubes_pgadmin.baza.state_out_flat["имя_ключа"] +nubes_pgadmin.baza.vault_secrets["имя_ключа"] +nubes_pgadmin.baza.vault_url +nubes_pgadmin.baza.vault_user_path +nubes_pgadmin.baza.vault_fields +``` diff --git a/generated/prod/docs/pgadmin_ops.md b/generated/prod/docs/pgadmin_ops.md new file mode 100644 index 0000000..7449f5e --- /dev/null +++ b/generated/prod/docs/pgadmin_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](pgadmin_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](pgadmin_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/prod/docs/pgadmin_outputs.md b/generated/prod/docs/pgadmin_outputs.md new file mode 100644 index 0000000..a88297a --- /dev/null +++ b/generated/prod/docs/pgadmin_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/prod/docs/pgadmin_params.md b/generated/prod/docs/pgadmin_params.md new file mode 100644 index 0000000..3afd95c --- /dev/null +++ b/generated/prod/docs/pgadmin_params.md @@ -0,0 +1,12 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Parameters + +- [Create params](pgadmin_params_create.md) +- [Modify params](pgadmin_params_modify.md) diff --git a/generated/prod/docs/pgadmin_params_create.md b/generated/prod/docs/pgadmin_params_create.md new file mode 100644 index 0000000..d527c72 --- /dev/null +++ b/generated/prod/docs/pgadmin_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
169resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
170loginstringЛогин для подключения к pgAdmin, в формате email (login@email.net)regex=^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$; maxlength=255; minlength=3
171passwordstringПароль для подключения к pgAdmin
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
164domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
165resource_cpuinteger200Квота ядра пода (milicores). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
166resource_memoryinteger256Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
167resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/pgadmin_params_modify.md b/generated/prod/docs/pgadmin_params_modify.md new file mode 100644 index 0000000..0b7613e --- /dev/null +++ b/generated/prod/docs/pgadmin_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
172resource_cpuintegerКвота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
173resource_memoryintegerКвота памяти пода (MBytes)minvalue=128; maxvalue=8192
174resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
diff --git a/generated/prod/docs/postgres.md b/generated/prod/docs/postgres.md new file mode 100644 index 0000000..61e7668 --- /dev/null +++ b/generated/prod/docs/postgres.md @@ -0,0 +1,13 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## MAN + +
+# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
+
diff --git a/generated/prod/docs/postgres_example.md b/generated/prod/docs/postgres_example.md new file mode 100644 index 0000000..fea5133 --- /dev/null +++ b/generated/prod/docs/postgres_example.md @@ -0,0 +1,63 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Copy-ready manifest (`postgres_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_postgres" "baza" { + s3_uid = "TODO" # Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_instances = 1 # Количество узлов + resource_memory = 512 # Квота памяти пода (MBytes) + resource_cpu = 500 # Квота ядра пода (milicores) + resource_disk = 10 # Квота диска (Указывается в GIGAbytes) + need_external_address_master = false # Выделяет внешний (белый) IP для доступа к master + need_external_address_slave = false # Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть + ext_backup_schedule = "0 0 * * *" # Не обязательный. Время запуска бекапа + ext_backup_num_to_retain = 7 # Не обязательный. Количество сохраняемых бекапов + app_version = 17 # Версия PostgreSQL + json_parameters = { "log_connections": "off", "log_disconnections": "off" } # Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html + enable_pg_pooler_master = false # Включает pgPooler (на запись). Будет создан новый под + enable_pg_pooler_slave = false # Включает pgPooler (на чтение). Будет создан новый под + allow_no_ssl = false # По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр + auto_scale = false # Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + auto_scale_percentage = 10 # Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + auto_scale_tech_window = 0 # Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + auto_scale_quota_gb = 1 # Квота для autoScale +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_postgres.baza.state_params["имя_ключа"] +nubes_postgres.baza.state_out["имя_ключа"] +nubes_postgres.baza.state_params_flat["имя_ключа"] +nubes_postgres.baza.state_out_flat["имя_ключа"] +nubes_postgres.baza.vault_secrets["имя_ключа"] +nubes_postgres.baza.vault_url +nubes_postgres.baza.vault_user_path +nubes_postgres.baza.vault_fields +``` diff --git a/generated/prod/docs/postgres_ops.md b/generated/prod/docs/postgres_ops.md new file mode 100644 index 0000000..ebe3b5c --- /dev/null +++ b/generated/prod/docs/postgres_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](postgres_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](postgres_params_modify.md) +- `recovery` +- `restart` — ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов. Может быть полная поломка. Крайне осторожно запускать при работе +- `resume` +- `suspend` diff --git a/generated/prod/docs/postgres_outputs.md b/generated/prod/docs/postgres_outputs.md new file mode 100644 index 0000000..f07b697 --- /dev/null +++ b/generated/prod/docs/postgres_outputs.md @@ -0,0 +1,65 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.master"]` +- `state_out_flat["externalConnect.master.fqdn"]` +- `state_out_flat["externalConnect.master.ip"]` +- `state_out_flat["externalConnect.master.isExternal"]` +- `state_out_flat["externalConnect.master.uuid"]` +- `state_out_flat["externalConnect.slave"]` +- `state_out_flat["externalConnect.slave.fqdn"]` +- `state_out_flat["externalConnect.slave.ip"]` +- `state_out_flat["externalConnect.slave.isExternal"]` +- `state_out_flat["externalConnect.slave.uuid"]` +- `state_out_flat["internalConnect"]` +- `state_out_flat["internalConnect.master"]` +- `state_out_flat["internalConnect.slave"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.k8spostgresql"]` +- `state_out_flat["monitoring.k8spostgresql.url"]` +- `state_out_flat["monitoring.k8spostgresqlbackup"]` +- `state_out_flat["monitoring.k8spostgresqlbackup.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` +- `vault_secrets["standbyPass"]` +- `vault_secrets["standbyUser"]` + +### Пример для связки с Lucee/NodeJS + +```hcl +testds_connectionString = "jdbc:postgresql://${nubes_postgres.db2.state_out_flat["internalConnect.master"]}:5432/postgres?sslmode=require" +testds_username = nubes_postgres.db2.vault_secrets["adminUser"] +testds_password = nubes_postgres.db2.vault_secrets["adminPass"] +``` diff --git a/generated/prod/docs/postgres_params.md b/generated/prod/docs/postgres_params.md new file mode 100644 index 0000000..b44e9f4 --- /dev/null +++ b/generated/prod/docs/postgres_params.md @@ -0,0 +1,12 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Parameters + +- [Create params](postgres_params_create.md) +- [Modify params](postgres_params_modify.md) diff --git a/generated/prod/docs/postgres_params_create.md b/generated/prod/docs/postgres_params_create.md new file mode 100644 index 0000000..69a72bb --- /dev/null +++ b/generated/prod/docs/postgres_params_create.md @@ -0,0 +1,55 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
23s3_uiduuidЭкземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательноref_svc_id=12
102resource_realmstringКластер Kubernetes, на котором будет развернут экземплярfunc=getAvailableResourceRealms
337ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`
338ip_space_name_slavestringИмя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
80resource_instancesinteger1Количество узлов в кластере. 1,3,5,7minvalue=1; maxvalue=7; regex=^[1357]$
81resource_memoryinteger512Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
82resource_cpuinteger500Квота ядра пода (milicores)minvalue=100; maxvalue=4000
83resource_disk10Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
145need_external_address_masterbooleanfalseВыделяет внешний (белый) IP для доступа к mastervalue_list=false, true
146need_external_address_slavebooleanfalseВыделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе естьvalue_list=false, true
265ext_backup_schedulestring0 0 * * *Не обязательный. Время запуска бекапаregex=^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$
266ext_backup_num_to_retaininteger7Не обязательный. Количество сохраняемых бекаповminvalue=1; maxvalue=99
310app_versionstring17Версия PostgreSQLvalue_list=17, 16, 15, 14, 13
311json_parametersjson{ "log_connections": "off", "log_disconnections": "off" }Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.htmlmaxlength=2048
312enable_pg_pooler_masterbooleanfalseВключает pgPooler (на запись). Будет создан новый подvalue_list=false, true
313enable_pg_pooler_slavebooleanfalseВключает pgPooler (на чтение). Будет создан новый подvalue_list=false, true
314allow_no_sslbooleanfalseПо умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметрvalue_list=false, true
329auto_scalebooleanfalseВключает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услугиvalue_list=false, true
330auto_scale_percentageinteger10Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объемаvalue_list=10, 15, 20
331auto_scale_tech_windowinteger0Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)value_list=0, 5, 12, 23
339auto_scale_quota_gbstring1Квота для autoScale
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/postgres_params_modify.md b/generated/prod/docs/postgres_params_modify.md new file mode 100644 index 0000000..2fcc3af --- /dev/null +++ b/generated/prod/docs/postgres_params_modify.md @@ -0,0 +1,29 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Modify params + + + + + + + + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
91resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
92resource_memoryintegerКвота памяти пода (MBytes)minvalue=128; maxvalue=8192
93resource_cpuintegerКвота ядра пода (milicores)minvalue=100; maxvalue=4000
94resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
147need_external_address_masterbooleanВыделяет внешний IP для доступа к mastervalue_list=false, true
148need_external_address_slavebooleanВыделяет внешний IP для доступа к slave. Действует, если slave в принципе естьvalue_list=false, true
267ext_backup_schedulestringНе обязательный. Время запуска бекапаregex=^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$
268ext_backup_num_to_retainintegerНе обязательный. Количество сохраняемых бекаповminvalue=1; maxvalue=99
315app_versionstringВерсия PostgreSQL. Нельзя опускать внизvalue_list=17, 16, 15, 14, 13
316json_parametersjsonМассив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.htmlmaxlength=2048
317enable_pg_pooler_masterbooleanВключает pgPooler (на запись). Будет создан новый подvalue_list=false, true
318enable_pg_pooler_slavebooleanВключает pgPooler (на чтение). Будет создан новый подvalue_list=false, true
319allow_no_sslbooleanПо умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметрvalue_list=false, true
541ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`
542ip_space_name_slavestringИмя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave
diff --git a/generated/prod/docs/rabbitmq.md b/generated/prod/docs/rabbitmq.md new file mode 100644 index 0000000..d0e5af3 --- /dev/null +++ b/generated/prod/docs/rabbitmq.md @@ -0,0 +1,13 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## MAN + +
+# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

--- +
diff --git a/generated/prod/docs/rabbitmq_example.md b/generated/prod/docs/rabbitmq_example.md new file mode 100644 index 0000000..f1a4f26 --- /dev/null +++ b/generated/prod/docs/rabbitmq_example.md @@ -0,0 +1,50 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Copy-ready manifest (`rabbitmq_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_rabbitmq" "baza" { + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 1000 # Пример: `2000` + resource_memory = 1124 # Пример: `2048` + resource_disk = 30 # Пример: `20` + resource_instances = 1 # Пример: `1` + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_rabbitmq.baza.state_params["имя_ключа"] +nubes_rabbitmq.baza.state_out["имя_ключа"] +nubes_rabbitmq.baza.state_params_flat["имя_ключа"] +nubes_rabbitmq.baza.state_out_flat["имя_ключа"] +nubes_rabbitmq.baza.vault_secrets["имя_ключа"] +nubes_rabbitmq.baza.vault_url +nubes_rabbitmq.baza.vault_user_path +nubes_rabbitmq.baza.vault_fields +``` diff --git a/generated/prod/docs/rabbitmq_ops.md b/generated/prod/docs/rabbitmq_ops.md new file mode 100644 index 0000000..fe63cf6 --- /dev/null +++ b/generated/prod/docs/rabbitmq_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](rabbitmq_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](rabbitmq_params_modify.md) +- `resume` +- `suspend` — Пока не работает из за того что RabbitmqCluster нельзя занулить diff --git a/generated/prod/docs/rabbitmq_outputs.md b/generated/prod/docs/rabbitmq_outputs.md new file mode 100644 index 0000000..30d870e --- /dev/null +++ b/generated/prod/docs/rabbitmq_outputs.md @@ -0,0 +1,48 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["clusterDomain"]` +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.admin"]` +- `state_out_flat["externalConnect.admin.fqdn"]` +- `state_out_flat["externalConnect.admin.ip"]` +- `state_out_flat["externalConnect.admin.isExternal"]` +- `state_out_flat["externalConnect.admin.uuid"]` +- `state_out_flat["inernalConnect"]` +- `state_out_flat["inernalConnect.master"]` +- `state_out_flat["inernalConnect.slave"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` + diff --git a/generated/prod/docs/rabbitmq_params.md b/generated/prod/docs/rabbitmq_params.md new file mode 100644 index 0000000..b743d18 --- /dev/null +++ b/generated/prod/docs/rabbitmq_params.md @@ -0,0 +1,12 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Parameters + +- [Create params](rabbitmq_params_create.md) +- [Modify params](rabbitmq_params_modify.md) diff --git a/generated/prod/docs/rabbitmq_params_create.md b/generated/prod/docs/rabbitmq_params_create.md new file mode 100644 index 0000000..1a1b21f --- /dev/null +++ b/generated/prod/docs/rabbitmq_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
119resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
543ip_space_name_masterstringПример: `internet-no-antiddos-v1`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
116resource_cpuinteger1000Пример: `2000`minvalue=500; maxvalue=4000
117resource_memoryinteger1124Пример: `2048`minvalue=512; maxvalue=8192
118resource_diskinteger30Пример: `20`minvalue=5; maxvalue=999
224resource_instancesinteger1Пример: `1`minvalue=1; maxvalue=7
225need_external_address_masterbooleanfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`value_list=false, true
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/rabbitmq_params_modify.md b/generated/prod/docs/rabbitmq_params_modify.md new file mode 100644 index 0000000..bdd6224 --- /dev/null +++ b/generated/prod/docs/rabbitmq_params_modify.md @@ -0,0 +1,22 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Modify params + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
304resource_cpuintegerПример: `2000`minvalue=500; maxvalue=4000
305resource_memoryintegerПример: `2048`minvalue=512; maxvalue=8192
306resource_diskintegerПример: `20`minvalue=1; maxvalue=999
307resource_instancesintegerПример: `1`minvalue=1; maxvalue=7
308need_external_address_masterbooleanВыделяет внешний (белый) IP для доступа к mastervalue_list=false, true
309need_external_address_slavebooleanВыделяет внешний (белый) IP для доступа к slavevalue_list=false, true
545ip_space_name_masterstringПример: `internet-no-antiddos-v1`
546ip_space_name_slavestringПример: `internet-no-antiddos-v1`
diff --git a/generated/prod/docs/redis.md b/generated/prod/docs/redis.md new file mode 100644 index 0000000..4dfd2ea --- /dev/null +++ b/generated/prod/docs/redis.md @@ -0,0 +1,13 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## MAN + +
+Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379 +
diff --git a/generated/prod/docs/redis_example.md b/generated/prod/docs/redis_example.md new file mode 100644 index 0000000..c1dd1d9 --- /dev/null +++ b/generated/prod/docs/redis_example.md @@ -0,0 +1,51 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Copy-ready manifest (`redis_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_redis" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 1000 # Квота ядра пода (Указывается в mili) + resource_memory = 1024 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 30 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + need_external_address_master = false # Выделяет внешний IP для доступа к master + need_external_address_slave = false # Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_redis.baza.state_params["имя_ключа"] +nubes_redis.baza.state_out["имя_ключа"] +nubes_redis.baza.state_params_flat["имя_ключа"] +nubes_redis.baza.state_out_flat["имя_ключа"] +nubes_redis.baza.vault_secrets["имя_ключа"] +nubes_redis.baza.vault_url +nubes_redis.baza.vault_user_path +nubes_redis.baza.vault_fields +``` diff --git a/generated/prod/docs/redis_ops.md b/generated/prod/docs/redis_ops.md new file mode 100644 index 0000000..d5d813c --- /dev/null +++ b/generated/prod/docs/redis_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](redis_params_create.md) +- `delete` diff --git a/generated/prod/docs/redis_outputs.md b/generated/prod/docs/redis_outputs.md new file mode 100644 index 0000000..c735f67 --- /dev/null +++ b/generated/prod/docs/redis_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/redis_params.md b/generated/prod/docs/redis_params.md new file mode 100644 index 0000000..131dd5e --- /dev/null +++ b/generated/prod/docs/redis_params.md @@ -0,0 +1,12 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Parameters + +- [Create params](redis_params_create.md) +- [Modify params](redis_params_modify.md) diff --git a/generated/prod/docs/redis_params_create.md b/generated/prod/docs/redis_params_create.md new file mode 100644 index 0000000..2dccb7e --- /dev/null +++ b/generated/prod/docs/redis_params_create.md @@ -0,0 +1,31 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
182resource_realmstringНаименование кластера Kubernetes для подключения
func=getAvailableResourceRealms
548ip_space_name_masterstringИмя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster
549ip_space_name_slavestringИмя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
112resource_cpuinteger1000Квота ядра пода (Указывается в mili)minvalue=500; maxvalue=4000
113resource_memoryinteger1024Квота памяти пода (Указывается в Megabytes)minvalue=512; maxvalue=8192
114resource_diskinteger30Квота диска (Указывается в GIGAbytes)minvalue=10; maxvalue=999
181resource_instancesinteger1Количество узлов в сети. Если выделить больше 1 ноды, будет добавлено дополнительно 3 Sentinel нодыminvalue=1; maxvalue=7
183need_external_address_masterbooleanfalseВыделяет внешний IP для доступа к mastervalue_list=false, true
184need_external_address_slavebooleanfalseВыделяет внешний IP для доступа к slave. Будет работать если slave в целом естьvalue_list=false, true
diff --git a/generated/prod/docs/redis_params_modify.md b/generated/prod/docs/redis_params_modify.md new file mode 100644 index 0000000..7461280 --- /dev/null +++ b/generated/prod/docs/redis_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/s3.md b/generated/prod/docs/s3.md new file mode 100644 index 0000000..15a87a0 --- /dev/null +++ b/generated/prod/docs/s3.md @@ -0,0 +1,13 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## MAN + +
+Руководство доступно по ссылке:
https://docs.s3.msk-1.ngcloud.ru/s3/s3.description.html +
diff --git a/generated/prod/docs/s3_example.md b/generated/prod/docs/s3_example.md new file mode 100644 index 0000000..0c02b26 --- /dev/null +++ b/generated/prod/docs/s3_example.md @@ -0,0 +1,44 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Copy-ready manifest (`s3_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_s3" "baza" { + resource_realm = "TODO" # Платформа для развертывания + display_name = "TODO" # Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_s3.baza.state_params["имя_ключа"] +nubes_s3.baza.state_out["имя_ключа"] +nubes_s3.baza.state_params_flat["имя_ключа"] +nubes_s3.baza.state_out_flat["имя_ключа"] +nubes_s3.baza.vault_secrets["имя_ключа"] +nubes_s3.baza.vault_url +nubes_s3.baza.vault_user_path +nubes_s3.baza.vault_fields +``` diff --git a/generated/prod/docs/s3_ops.md b/generated/prod/docs/s3_ops.md new file mode 100644 index 0000000..d030e69 --- /dev/null +++ b/generated/prod/docs/s3_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](s3_params_create.md) +- `create_sub_user` — Создание кластера. Параметры: [Create params](s3_params_create.md) +- `delete` +- `delete_sub_user` +- `modify` — Изменение кластера. Параметры: [Modify params](s3_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/s3_outputs.md b/generated/prod/docs/s3_outputs.md new file mode 100644 index 0000000..e1cdeb2 --- /dev/null +++ b/generated/prod/docs/s3_outputs.md @@ -0,0 +1,42 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dtEndTrial"]` +- `state_out_flat["dtStartTrial"]` +- `state_out_flat["isTrial"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.s3ceph"]` +- `state_out_flat["monitoring.s3ceph.url"]` +- `state_out_flat["urlConnect"]` + +### `vault_secrets` ключи + +- `vault_secrets["1325_access_key"]` +- `vault_secrets["1325_secret_key"]` +- `vault_secrets["1325_technicalS3Backup_access_key"]` +- `vault_secrets["1325_technicalS3Backup_secret_key"]` + diff --git a/generated/prod/docs/s3_params.md b/generated/prod/docs/s3_params.md new file mode 100644 index 0000000..d61aced --- /dev/null +++ b/generated/prod/docs/s3_params.md @@ -0,0 +1,12 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Parameters + +- [Create params](s3_params_create.md) +- [Modify params](s3_params_modify.md) diff --git a/generated/prod/docs/s3_params_create.md b/generated/prod/docs/s3_params_create.md new file mode 100644 index 0000000..743e44f --- /dev/null +++ b/generated/prod/docs/s3_params_create.md @@ -0,0 +1,33 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
163resource_realmкластер S3 для подключенияfunc=getAvailableResourceRealms
320display_namestringЧеловеко-читаемое имя (транслит из Elma) - передаётся имя клиента
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/s3_params_modify.md b/generated/prod/docs/s3_params_modify.md new file mode 100644 index 0000000..d9859fc --- /dev/null +++ b/generated/prod/docs/s3_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
50max_size_gb_per_userintegermaxSizeGb. Передаём рукамиminvalue=-1; maxvalue=50000
51max_objects_per_bucketintegerКвота на бакет. Кол-во объектов. Передаём рукамиminvalue=1; maxvalue=10000000
52max_buckets_per_userintegerКвота на пользователя. Количество бакетов пользователя. Передаём рукамиminvalue=1; maxvalue=1000
diff --git a/generated/prod/docs/s3bucket.md b/generated/prod/docs/s3bucket.md new file mode 100644 index 0000000..27d17d7 --- /dev/null +++ b/generated/prod/docs/s3bucket.md @@ -0,0 +1,13 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/s3bucket_example.md b/generated/prod/docs/s3bucket_example.md new file mode 100644 index 0000000..233571e --- /dev/null +++ b/generated/prod/docs/s3bucket_example.md @@ -0,0 +1,51 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Copy-ready manifest (`s3bucket_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_s3bucket" "baza" { + s3_user_uid = "TODO" # UID Корневой услуги S3. На основе него будет создан бакет. + bucket_name = "TODO" # Имя бакета s3 + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + max_size = -1 # Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes + read_all = false # Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. + list_all = false # Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. + cors_all = false # Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS + placement = "HOT" # Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_s3bucket.baza.state_params["имя_ключа"] +nubes_s3bucket.baza.state_out["имя_ключа"] +nubes_s3bucket.baza.state_params_flat["имя_ключа"] +nubes_s3bucket.baza.state_out_flat["имя_ключа"] +nubes_s3bucket.baza.vault_secrets["имя_ключа"] +nubes_s3bucket.baza.vault_url +nubes_s3bucket.baza.vault_user_path +nubes_s3bucket.baza.vault_fields +``` diff --git a/generated/prod/docs/s3bucket_ops.md b/generated/prod/docs/s3bucket_ops.md new file mode 100644 index 0000000..61b2abb --- /dev/null +++ b/generated/prod/docs/s3bucket_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](s3bucket_params_create.md) +- `delete` diff --git a/generated/prod/docs/s3bucket_outputs.md b/generated/prod/docs/s3bucket_outputs.md new file mode 100644 index 0000000..f62a754 --- /dev/null +++ b/generated/prod/docs/s3bucket_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["httpConnect"]` + diff --git a/generated/prod/docs/s3bucket_params.md b/generated/prod/docs/s3bucket_params.md new file mode 100644 index 0000000..9977e26 --- /dev/null +++ b/generated/prod/docs/s3bucket_params.md @@ -0,0 +1,12 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Parameters + +- [Create params](s3bucket_params_create.md) +- [Modify params](s3bucket_params_modify.md) diff --git a/generated/prod/docs/s3bucket_params_create.md b/generated/prod/docs/s3bucket_params_create.md new file mode 100644 index 0000000..0ae6f44 --- /dev/null +++ b/generated/prod/docs/s3bucket_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
124s3_user_uiduuidUID Корневой услуги S3. На основе него будет создан бакет.ref_svc_id=12
125bucket_namestringИмя бакета s3
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
126max_size-1Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes
127read_allbooleanfalseПубличный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета.value_list=false, true
128list_allbooleanfalseПубличный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете.value_list=false, true
129cors_allbooleanfalseПубличный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORSvalue_list=false, true
130placementstringHOTКласс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращенияvalue_list=HOT, COLD
diff --git a/generated/prod/docs/s3bucket_params_modify.md b/generated/prod/docs/s3bucket_params_modify.md new file mode 100644 index 0000000..ca24765 --- /dev/null +++ b/generated/prod/docs/s3bucket_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/superset.md b/generated/prod/docs/superset.md new file mode 100644 index 0000000..93094e9 --- /dev/null +++ b/generated/prod/docs/superset.md @@ -0,0 +1,13 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## MAN + +
+Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru) +
diff --git a/generated/prod/docs/superset_example.md b/generated/prod/docs/superset_example.md new file mode 100644 index 0000000..ce560e5 --- /dev/null +++ b/generated/prod/docs/superset_example.md @@ -0,0 +1,51 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Copy-ready manifest (`superset_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_superset" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + emails = "testuser1" # Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_superset.baza.state_params["имя_ключа"] +nubes_superset.baza.state_out["имя_ключа"] +nubes_superset.baza.state_params_flat["имя_ключа"] +nubes_superset.baza.state_out_flat["имя_ключа"] +nubes_superset.baza.vault_secrets["имя_ключа"] +nubes_superset.baza.vault_url +nubes_superset.baza.vault_user_path +nubes_superset.baza.vault_fields +``` diff --git a/generated/prod/docs/superset_ops.md b/generated/prod/docs/superset_ops.md new file mode 100644 index 0000000..5aa441d --- /dev/null +++ b/generated/prod/docs/superset_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](superset_params_create.md) +- `delete` — DEV-Ветка удаления Superset diff --git a/generated/prod/docs/superset_outputs.md b/generated/prod/docs/superset_outputs.md new file mode 100644 index 0000000..4d7a1fd --- /dev/null +++ b/generated/prod/docs/superset_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/superset_params.md b/generated/prod/docs/superset_params.md new file mode 100644 index 0000000..2984f1c --- /dev/null +++ b/generated/prod/docs/superset_params.md @@ -0,0 +1,12 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Parameters + +- [Create params](superset_params_create.md) +- [Modify params](superset_params_modify.md) diff --git a/generated/prod/docs/superset_params_create.md b/generated/prod/docs/superset_params_create.md new file mode 100644 index 0000000..6652cf1 --- /dev/null +++ b/generated/prod/docs/superset_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
109resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
14domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
20emailsstringtestuser1Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел
96resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядроminvalue=100; maxvalue=4000
97resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
98resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
227resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
diff --git a/generated/prod/docs/superset_params_modify.md b/generated/prod/docs/superset_params_modify.md new file mode 100644 index 0000000..5a6e1ca --- /dev/null +++ b/generated/prod/docs/superset_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/template.md b/generated/prod/docs/template.md new file mode 100644 index 0000000..b4d0dc3 --- /dev/null +++ b/generated/prod/docs/template.md @@ -0,0 +1,13 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/template_example.md b/generated/prod/docs/template_example.md new file mode 100644 index 0000000..e00dd7c --- /dev/null +++ b/generated/prod/docs/template_example.md @@ -0,0 +1,50 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Copy-ready manifest (`template_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_template" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 100 + resource_memory = 200 + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_template.baza.state_params["имя_ключа"] +nubes_template.baza.state_out["имя_ключа"] +nubes_template.baza.state_params_flat["имя_ключа"] +nubes_template.baza.state_out_flat["имя_ключа"] +nubes_template.baza.vault_secrets["имя_ключа"] +nubes_template.baza.vault_url +nubes_template.baza.vault_user_path +nubes_template.baza.vault_fields +``` diff --git a/generated/prod/docs/template_ops.md b/generated/prod/docs/template_ops.md new file mode 100644 index 0000000..7bea1f5 --- /dev/null +++ b/generated/prod/docs/template_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](template_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](template_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/template_outputs.md b/generated/prod/docs/template_outputs.md new file mode 100644 index 0000000..ab5623d --- /dev/null +++ b/generated/prod/docs/template_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/template_params.md b/generated/prod/docs/template_params.md new file mode 100644 index 0000000..7fdd8d3 --- /dev/null +++ b/generated/prod/docs/template_params.md @@ -0,0 +1,12 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Parameters + +- [Create params](template_params_create.md) +- [Modify params](template_params_modify.md) diff --git a/generated/prod/docs/template_params_create.md b/generated/prod/docs/template_params_create.md new file mode 100644 index 0000000..aaa56ea --- /dev/null +++ b/generated/prod/docs/template_params_create.md @@ -0,0 +1,40 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
206resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
202resource_cpuinteger100minvalue=100; maxvalue=4000
203resource_memoryinteger200minvalue=128; maxvalue=8192
204resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
205resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
207domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/template_params_modify.md b/generated/prod/docs/template_params_modify.md new file mode 100644 index 0000000..9e15054 --- /dev/null +++ b/generated/prod/docs/template_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
208resource_cpuintegerminvalue=100; maxvalue=4000
209resource_memoryintegerminvalue=128; maxvalue=8192
210resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
211resource_instancesintegerКоличество узловminvalue=1; maxvalue=7
diff --git a/generated/prod/docs/tenant.md b/generated/prod/docs/tenant.md new file mode 100644 index 0000000..409820a --- /dev/null +++ b/generated/prod/docs/tenant.md @@ -0,0 +1,13 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/tenant_example.md b/generated/prod/docs/tenant_example.md new file mode 100644 index 0000000..b656a0d --- /dev/null +++ b/generated/prod/docs/tenant_example.md @@ -0,0 +1,44 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Copy-ready manifest (`tenant_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_tenant" "baza" { + grafana_org_name = "TODO" # WZ-Код клиента + resource_realm = "TODO" # Платформа для развертывания +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_tenant.baza.state_params["имя_ключа"] +nubes_tenant.baza.state_out["имя_ключа"] +nubes_tenant.baza.state_params_flat["имя_ключа"] +nubes_tenant.baza.state_out_flat["имя_ключа"] +nubes_tenant.baza.vault_secrets["имя_ключа"] +nubes_tenant.baza.vault_url +nubes_tenant.baza.vault_user_path +nubes_tenant.baza.vault_fields +``` diff --git a/generated/prod/docs/tenant_ops.md b/generated/prod/docs/tenant_ops.md new file mode 100644 index 0000000..5058136 --- /dev/null +++ b/generated/prod/docs/tenant_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](tenant_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](tenant_params_modify.md) diff --git a/generated/prod/docs/tenant_outputs.md b/generated/prod/docs/tenant_outputs.md new file mode 100644 index 0000000..4ebe277 --- /dev/null +++ b/generated/prod/docs/tenant_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/tenant_params.md b/generated/prod/docs/tenant_params.md new file mode 100644 index 0000000..4db7d6b --- /dev/null +++ b/generated/prod/docs/tenant_params.md @@ -0,0 +1,12 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Parameters + +- [Create params](tenant_params_create.md) +- [Modify params](tenant_params_modify.md) diff --git a/generated/prod/docs/tenant_params_create.md b/generated/prod/docs/tenant_params_create.md new file mode 100644 index 0000000..10d4d27 --- /dev/null +++ b/generated/prod/docs/tenant_params_create.md @@ -0,0 +1,21 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
239grafana_org_namestringWZ-Код клиентаregex=^WZ[\d]+$
240resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/prod/docs/tenant_params_modify.md b/generated/prod/docs/tenant_params_modify.md new file mode 100644 index 0000000..cc3e107 --- /dev/null +++ b/generated/prod/docs/tenant_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/valo_tenant.md b/generated/prod/docs/valo_tenant.md new file mode 100644 index 0000000..6860e2f --- /dev/null +++ b/generated/prod/docs/valo_tenant.md @@ -0,0 +1,13 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## MAN + +
+# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
+
diff --git a/generated/prod/docs/valo_tenant_example.md b/generated/prod/docs/valo_tenant_example.md new file mode 100644 index 0000000..44a3695 --- /dev/null +++ b/generated/prod/docs/valo_tenant_example.md @@ -0,0 +1,48 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Copy-ready manifest (`valo_tenant_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_valo_tenant" "baza" { + resource_realm = "TODO" + domain = "TODO" # Будет добавлено в зону valo.ngcloud.ru + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + valo_options = {} + s3_storage = 10 # Параметр указывается в `Gb` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_valo_tenant.baza.state_params["имя_ключа"] +nubes_valo_tenant.baza.state_out["имя_ключа"] +nubes_valo_tenant.baza.state_params_flat["имя_ключа"] +nubes_valo_tenant.baza.state_out_flat["имя_ключа"] +nubes_valo_tenant.baza.vault_secrets["имя_ключа"] +nubes_valo_tenant.baza.vault_url +nubes_valo_tenant.baza.vault_user_path +nubes_valo_tenant.baza.vault_fields +``` diff --git a/generated/prod/docs/valo_tenant_ops.md b/generated/prod/docs/valo_tenant_ops.md new file mode 100644 index 0000000..89cb168 --- /dev/null +++ b/generated/prod/docs/valo_tenant_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](valo_tenant_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](valo_tenant_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/valo_tenant_outputs.md b/generated/prod/docs/valo_tenant_outputs.md new file mode 100644 index 0000000..2d4ea5d --- /dev/null +++ b/generated/prod/docs/valo_tenant_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/valo_tenant_params.md b/generated/prod/docs/valo_tenant_params.md new file mode 100644 index 0000000..2c3d14f --- /dev/null +++ b/generated/prod/docs/valo_tenant_params.md @@ -0,0 +1,12 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Parameters + +- [Create params](valo_tenant_params_create.md) +- [Modify params](valo_tenant_params_modify.md) diff --git a/generated/prod/docs/valo_tenant_params_create.md b/generated/prod/docs/valo_tenant_params_create.md new file mode 100644 index 0000000..a0e9538 --- /dev/null +++ b/generated/prod/docs/valo_tenant_params_create.md @@ -0,0 +1,38 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
655resource_realmstringfunc=getAvailableResourceRealms
656domainstringБудет добавлено в зону valo.ngcloud.ruregex=^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$; maxlength=61; minlength=3
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
657valo_optionsmap-fixed{}
658s3_storageinteger10Данные хранятся в отдельном экземпляре S3minvalue=1; maxvalue=999
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/valo_tenant_params_modify.md b/generated/prod/docs/valo_tenant_params_modify.md new file mode 100644 index 0000000..4b07bec --- /dev/null +++ b/generated/prod/docs/valo_tenant_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
659valo_optionsmap-fixed
660s3_storageintegerДанные хранятся в отдельном экземпляре S3
diff --git a/generated/prod/docs/vapp.md b/generated/prod/docs/vapp.md new file mode 100644 index 0000000..630f47c --- /dev/null +++ b/generated/prod/docs/vapp.md @@ -0,0 +1,13 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## MAN + +
+# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции. +
diff --git a/generated/prod/docs/vapp_example.md b/generated/prod/docs/vapp_example.md new file mode 100644 index 0000000..0400883 --- /dev/null +++ b/generated/prod/docs/vapp_example.md @@ -0,0 +1,45 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Copy-ready manifest (`vapp_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vapp" "baza" { + nsxt_uid = "TODO" # UUID Услуги `Сетевой шлюз периметра (Edge)` + vapp_name = "TODO" # Уникальное в рамках организации + vdc_uid = "TODO" # UUID Услуги `Виртуальный датацентр (vDC)` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vapp.baza.state_params["имя_ключа"] +nubes_vapp.baza.state_out["имя_ключа"] +nubes_vapp.baza.state_params_flat["имя_ключа"] +nubes_vapp.baza.state_out_flat["имя_ключа"] +nubes_vapp.baza.vault_secrets["имя_ключа"] +nubes_vapp.baza.vault_url +nubes_vapp.baza.vault_user_path +nubes_vapp.baza.vault_fields +``` diff --git a/generated/prod/docs/vapp_ops.md b/generated/prod/docs/vapp_ops.md new file mode 100644 index 0000000..5e1eba0 --- /dev/null +++ b/generated/prod/docs/vapp_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vapp_params_create.md) +- `delete` +- `resume` +- `suspend` diff --git a/generated/prod/docs/vapp_outputs.md b/generated/prod/docs/vapp_outputs.md new file mode 100644 index 0000000..0ff0f8e --- /dev/null +++ b/generated/prod/docs/vapp_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vapp_params.md b/generated/prod/docs/vapp_params.md new file mode 100644 index 0000000..4c7addc --- /dev/null +++ b/generated/prod/docs/vapp_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Parameters + +- [Create params](vapp_params_create.md) +- [Modify params](vapp_params_modify.md) diff --git a/generated/prod/docs/vapp_params_create.md b/generated/prod/docs/vapp_params_create.md new file mode 100644 index 0000000..3b23cff --- /dev/null +++ b/generated/prod/docs/vapp_params_create.md @@ -0,0 +1,34 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
190nsxt_uiduuidК какой сети может быть подключен виртуальный каталогref_svc_id=22
191vapp_namestringИмеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин)
regex=^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$; minlength=3
623vdc_uiduuidВ каких ресурсах будет создан виртуальный каталогref_svc_id=21
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/vapp_params_modify.md b/generated/prod/docs/vapp_params_modify.md new file mode 100644 index 0000000..4fca747 --- /dev/null +++ b/generated/prod/docs/vapp_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/vc_complex.md b/generated/prod/docs/vc_complex.md new file mode 100644 index 0000000..c83cece --- /dev/null +++ b/generated/prod/docs/vc_complex.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## MAN + +
+# Создает окружение для ВМ.

## Создает сервисы:
**Организация**
**Виртуальный датацентр**
**Каталог**
**Сетевой шлюз периметра**

+
diff --git a/generated/prod/docs/vc_complex_example.md b/generated/prod/docs/vc_complex_example.md new file mode 100644 index 0000000..56a54ba --- /dev/null +++ b/generated/prod/docs/vc_complex_example.md @@ -0,0 +1,59 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Copy-ready manifest (`vc_complex_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_complex" "baza" { + resource_realm = "TODO" # Платформа для развертывания + vapp_name = "TODO" # Имя vApp + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + vdc_provider_gateway = "sandbox-v1cl1-pvdc" # Взять из свойств ресурсной платформы. Путь .vcd.providerVdc - взять KEY providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String + virtual_services_count = 1 # Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 + ip_space_name = "internet-ipv4-v1" # Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT + storage_config = [{"name": "", "size": 1024}] # Описывается как массив объектов, где описывается имя политики и её размер + vdc_network_pool = "nsxt-sandbox-geneve-np" + cpu_guaranteed = 20 + mem_guaranteed = 20 + segroup_name = "SEGROUP-SANDBOX-CL1-SHARED-01" # Наименование segroup, в котором будут выделяться пулы на AVI + need_external_address_snat = true # Выделяет внешний IP на Edge. Используется для SNAT всего трафика + need_enable_avi = false # Включает AVI в Cloud Director + cpu_allocated = 1 # шт + mem_allocated = 10 # gb + v_ipcount = 0 # ш +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_complex.baza.state_params["имя_ключа"] +nubes_vc_complex.baza.state_out["имя_ключа"] +nubes_vc_complex.baza.state_params_flat["имя_ключа"] +nubes_vc_complex.baza.state_out_flat["имя_ключа"] +nubes_vc_complex.baza.vault_secrets["имя_ключа"] +nubes_vc_complex.baza.vault_url +nubes_vc_complex.baza.vault_user_path +nubes_vc_complex.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_complex_ops.md b/generated/prod/docs/vc_complex_ops.md new file mode 100644 index 0000000..9d016bc --- /dev/null +++ b/generated/prod/docs/vc_complex_ops.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_complex_params_create.md) diff --git a/generated/prod/docs/vc_complex_outputs.md b/generated/prod/docs/vc_complex_outputs.md new file mode 100644 index 0000000..629a059 --- /dev/null +++ b/generated/prod/docs/vc_complex_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vc_complex_params.md b/generated/prod/docs/vc_complex_params.md new file mode 100644 index 0000000..82b3c13 --- /dev/null +++ b/generated/prod/docs/vc_complex_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Parameters + +- [Create params](vc_complex_params_create.md) +- [Modify params](vc_complex_params_modify.md) diff --git a/generated/prod/docs/vc_complex_params_create.md b/generated/prod/docs/vc_complex_params_create.md new file mode 100644 index 0000000..6b0b9ec --- /dev/null +++ b/generated/prod/docs/vc_complex_params_create.md @@ -0,0 +1,37 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
332resource_realmstringНаименование ресурсной платформыfunc=getAvailableResourceRealms
334vapp_namestringИмя vAppregex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
358vdc_provider_gatewaystringsandbox-v1cl1-pvdcВзять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List&lt;String&gt;) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String
359virtual_services_count1Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4minvalue=1; maxvalue=4
360ip_space_namestringinternet-ipv4-v1Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT
399storage_configjson[{"name": "", "size": 1024}]Описывается как массив объектов, где описывается имя политики и её размер
400vdc_network_poolstringnsxt-sandbox-geneve-np
401cpu_guaranteedinteger20value_list=20, 50, 80
402mem_guaranteedinteger20value_list=20, 50, 80
403segroup_namestringSEGROUP-SANDBOX-CL1-SHARED-01Наименование segroup, в котором будут выделяться пулы на AVI
404need_external_address_snatbooleantrueВыделяет внешний IP на Edge. Используется для SNAT всего трафикаvalue_list=false, true
405need_enable_avibooleanfalseВключает AVI в Cloud Directorvalue_list=false, true
566cpu_allocatedstring1штminvalue=1
567mem_allocatedinteger10gbminvalue=1
568v_ipcountinteger0шminvalue=0; maxvalue=20
diff --git a/generated/prod/docs/vc_complex_params_modify.md b/generated/prod/docs/vc_complex_params_modify.md new file mode 100644 index 0000000..b6a1be3 --- /dev/null +++ b/generated/prod/docs/vc_complex_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/vc_nsxt.md b/generated/prod/docs/vc_nsxt.md new file mode 100644 index 0000000..0c8056d --- /dev/null +++ b/generated/prod/docs/vc_nsxt.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## MAN + +
+# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

+
diff --git a/generated/prod/docs/vc_nsxt_example.md b/generated/prod/docs/vc_nsxt_example.md new file mode 100644 index 0000000..8e0a04f --- /dev/null +++ b/generated/prod/docs/vc_nsxt_example.md @@ -0,0 +1,47 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Copy-ready manifest (`vc_nsxt_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_nsxt" "baza" { + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_enable_avi = false # Включение Load Balancer + virtual_services_count = 1 # Кол-во виртуальных сервисов, которые выделяются на AVI + vdc_type = "vdc" # Тип родительской услуги +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_nsxt.baza.state_params["имя_ключа"] +nubes_vc_nsxt.baza.state_out["имя_ключа"] +nubes_vc_nsxt.baza.state_params_flat["имя_ключа"] +nubes_vc_nsxt.baza.state_out_flat["имя_ключа"] +nubes_vc_nsxt.baza.vault_secrets["имя_ключа"] +nubes_vc_nsxt.baza.vault_url +nubes_vc_nsxt.baza.vault_user_path +nubes_vc_nsxt.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_nsxt_ops.md b/generated/prod/docs/vc_nsxt_ops.md new file mode 100644 index 0000000..03af84f --- /dev/null +++ b/generated/prod/docs/vc_nsxt_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_nsxt_params_create.md) +- `delete` — Невозможно удалить если routed-сеть в Edge связана с: - vApp - VM - Kubernetes-кластер Штурвал Запустить операцию возможно, но будет ошибка +- `modify` — Изменение кластера. Параметры: [Modify params](vc_nsxt_params_modify.md) +- `reconcile` diff --git a/generated/prod/docs/vc_nsxt_outputs.md b/generated/prod/docs/vc_nsxt_outputs.md new file mode 100644 index 0000000..717cbc4 --- /dev/null +++ b/generated/prod/docs/vc_nsxt_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vc_nsxt_params.md b/generated/prod/docs/vc_nsxt_params.md new file mode 100644 index 0000000..2cf1cc1 --- /dev/null +++ b/generated/prod/docs/vc_nsxt_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Parameters + +- [Create params](vc_nsxt_params_create.md) +- [Modify params](vc_nsxt_params_modify.md) diff --git a/generated/prod/docs/vc_nsxt_params_create.md b/generated/prod/docs/vc_nsxt_params_create.md new file mode 100644 index 0000000..9f77ac0 --- /dev/null +++ b/generated/prod/docs/vc_nsxt_params_create.md @@ -0,0 +1,27 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
8vdc_uidstringНеобходимо, если выбран тип подключаемой VDC: `vdc`ref_svc_id=21
622vdc_group_uidstringНеобходимо, если выбран тип подключаемой VDC: `groupvdc`ref_svc_id=29
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
340need_enable_avibooleanfalseПараметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указатьvalue_list=false, true
341virtual_services_countinteger1Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB`
minvalue=1; maxvalue=4
621vdc_typestringvdcПри выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc
value_list=vdc, vdcGroup
diff --git a/generated/prod/docs/vc_nsxt_params_modify.md b/generated/prod/docs/vc_nsxt_params_modify.md new file mode 100644 index 0000000..0a1fc0e --- /dev/null +++ b/generated/prod/docs/vc_nsxt_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
368need_enable_avibooleanПараметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указатьvalue_list=false, true
369virtual_services_countintegerВо избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB`
minvalue=1; maxvalue=4
371need_external_address_snatbooleanПараметр `ip Space для VIP` станет обязательным, если указан `true`value_list=false, true
372ip_space_namestringНеобходимо указывать, если включён параметр `Выделить VIP для SNAT`
diff --git a/generated/prod/docs/vc_org.md b/generated/prod/docs/vc_org.md new file mode 100644 index 0000000..e500201 --- /dev/null +++ b/generated/prod/docs/vc_org.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## MAN + +
+# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak. +
diff --git a/generated/prod/docs/vc_org_example.md b/generated/prod/docs/vc_org_example.md new file mode 100644 index 0000000..5785a4a --- /dev/null +++ b/generated/prod/docs/vc_org_example.md @@ -0,0 +1,46 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Copy-ready manifest (`vc_org_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_org" "baza" { + resource_realm = "TODO" # Облако в котором будет развёрнут тенант + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + organization_type = "iaas" # Доступ к организации предоставляется только при выборе `iaas` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_org.baza.state_params["имя_ключа"] +nubes_vc_org.baza.state_out["имя_ключа"] +nubes_vc_org.baza.state_params_flat["имя_ключа"] +nubes_vc_org.baza.state_out_flat["имя_ключа"] +nubes_vc_org.baza.vault_secrets["имя_ключа"] +nubes_vc_org.baza.vault_url +nubes_vc_org.baza.vault_user_path +nubes_vc_org.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_org_ops.md b/generated/prod/docs/vc_org_ops.md new file mode 100644 index 0000000..a0c2067 --- /dev/null +++ b/generated/prod/docs/vc_org_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_org_params_create.md) +- `delete` — Удалить возможно только через 14 дней после выполнения операции `suspend` **ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта** Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов +- `modify` — Изменение кластера. Параметры: [Modify params](vc_org_params_modify.md) +- `reconcile` +- `resume` +- `suspend` — Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта diff --git a/generated/prod/docs/vc_org_outputs.md b/generated/prod/docs/vc_org_outputs.md new file mode 100644 index 0000000..9de11b9 --- /dev/null +++ b/generated/prod/docs/vc_org_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["urlConnect"]` + diff --git a/generated/prod/docs/vc_org_params.md b/generated/prod/docs/vc_org_params.md new file mode 100644 index 0000000..e187b07 --- /dev/null +++ b/generated/prod/docs/vc_org_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Parameters + +- [Create params](vc_org_params_create.md) +- [Modify params](vc_org_params_modify.md) diff --git a/generated/prod/docs/vc_org_params_create.md b/generated/prod/docs/vc_org_params_create.md new file mode 100644 index 0000000..28f12f0 --- /dev/null +++ b/generated/prod/docs/vc_org_params_create.md @@ -0,0 +1,36 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
418resource_realmstringВыбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы
func=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
556organization_typestringiaasДоступ к организации предоставляется только при выборе `iaas`value_list=iaas, saas
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/vc_org_params_modify.md b/generated/prod/docs/vc_org_params_modify.md new file mode 100644 index 0000000..4142f72 --- /dev/null +++ b/generated/prod/docs/vc_org_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
662v_ipconfigurearray-map-fixed
diff --git a/generated/prod/docs/vc_org_saas.md b/generated/prod/docs/vc_org_saas.md new file mode 100644 index 0000000..95ae27c --- /dev/null +++ b/generated/prod/docs/vc_org_saas.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## MAN + +
+Верхнеуровневая сущность создаваемая для Заказчика.
Внутри Организация вы можете создать: Виртуальные датацентры, Каталоги. +
diff --git a/generated/prod/docs/vc_org_saas_example.md b/generated/prod/docs/vc_org_saas_example.md new file mode 100644 index 0000000..a531fe9 --- /dev/null +++ b/generated/prod/docs/vc_org_saas_example.md @@ -0,0 +1,43 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Copy-ready manifest (`vc_org_saas_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_org_saas" "baza" { + resource_realm = "TODO" # Платформа для развертывания +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_org_saas.baza.state_params["имя_ключа"] +nubes_vc_org_saas.baza.state_out["имя_ключа"] +nubes_vc_org_saas.baza.state_params_flat["имя_ключа"] +nubes_vc_org_saas.baza.state_out_flat["имя_ключа"] +nubes_vc_org_saas.baza.vault_secrets["имя_ключа"] +nubes_vc_org_saas.baza.vault_url +nubes_vc_org_saas.baza.vault_user_path +nubes_vc_org_saas.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_org_saas_ops.md b/generated/prod/docs/vc_org_saas_ops.md new file mode 100644 index 0000000..bcc4618 --- /dev/null +++ b/generated/prod/docs/vc_org_saas_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_org_saas_params_create.md) +- `delete` +- `resume` +- `suspend` diff --git a/generated/prod/docs/vc_org_saas_outputs.md b/generated/prod/docs/vc_org_saas_outputs.md new file mode 100644 index 0000000..15052c9 --- /dev/null +++ b/generated/prod/docs/vc_org_saas_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vc_org_saas_params.md b/generated/prod/docs/vc_org_saas_params.md new file mode 100644 index 0000000..a9c08f3 --- /dev/null +++ b/generated/prod/docs/vc_org_saas_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Parameters + +- [Create params](vc_org_saas_params_create.md) +- [Modify params](vc_org_saas_params_modify.md) diff --git a/generated/prod/docs/vc_org_saas_params_create.md b/generated/prod/docs/vc_org_saas_params_create.md new file mode 100644 index 0000000..d73866d --- /dev/null +++ b/generated/prod/docs/vc_org_saas_params_create.md @@ -0,0 +1,32 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
185resource_realmstringНаименование ресурсной платформыfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/vc_org_saas_params_modify.md b/generated/prod/docs/vc_org_saas_params_modify.md new file mode 100644 index 0000000..ad45c99 --- /dev/null +++ b/generated/prod/docs/vc_org_saas_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/vc_vdc.md b/generated/prod/docs/vc_vdc.md new file mode 100644 index 0000000..2d8fa57 --- /dev/null +++ b/generated/prod/docs/vc_vdc.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## MAN + +
+# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
+
diff --git a/generated/prod/docs/vc_vdc_example.md b/generated/prod/docs/vc_vdc_example.md new file mode 100644 index 0000000..7b6e1bc --- /dev/null +++ b/generated/prod/docs/vc_vdc_example.md @@ -0,0 +1,52 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Copy-ready manifest (`vc_vdc_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vdc" "baza" { + organization_uid = "TODO" # Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` + provider_vdc = "TODO" # Приписка `2.8` - Скорость vCPU - 2800MHz И так далее... + storage_config = "TODO" # Первая политика в списке указывается как "По умолчанию" + network_provider = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + cpu_guaranteed = 20 # % резервирования + mem_guaranteed = 20 # % резервирования + cpu_allocated = 10 # шт + mem_allocated = 20 # gb +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vdc.baza.state_params["имя_ключа"] +nubes_vc_vdc.baza.state_out["имя_ключа"] +nubes_vc_vdc.baza.state_params_flat["имя_ключа"] +nubes_vc_vdc.baza.state_out_flat["имя_ключа"] +nubes_vc_vdc.baza.vault_secrets["имя_ключа"] +nubes_vc_vdc.baza.vault_url +nubes_vc_vdc.baza.vault_user_path +nubes_vc_vdc.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_vdc_group.md b/generated/prod/docs/vc_vdc_group.md new file mode 100644 index 0000000..1ff3cd6 --- /dev/null +++ b/generated/prod/docs/vc_vdc_group.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## MAN + +
+# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc` +
diff --git a/generated/prod/docs/vc_vdc_group_example.md b/generated/prod/docs/vc_vdc_group_example.md new file mode 100644 index 0000000..0fecefd --- /dev/null +++ b/generated/prod/docs/vc_vdc_group_example.md @@ -0,0 +1,43 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Copy-ready manifest (`vc_vdc_group_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vdc_group" "baza" { + vdc_uid = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vdc_group.baza.state_params["имя_ключа"] +nubes_vc_vdc_group.baza.state_out["имя_ключа"] +nubes_vc_vdc_group.baza.state_params_flat["имя_ключа"] +nubes_vc_vdc_group.baza.state_out_flat["имя_ключа"] +nubes_vc_vdc_group.baza.vault_secrets["имя_ключа"] +nubes_vc_vdc_group.baza.vault_url +nubes_vc_vdc_group.baza.vault_user_path +nubes_vc_vdc_group.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_vdc_group_ops.md b/generated/prod/docs/vc_vdc_group_ops.md new file mode 100644 index 0000000..9552c15 --- /dev/null +++ b/generated/prod/docs/vc_vdc_group_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Operations + +- `add_vdc` — **Не нужно** выбирать UUID VDC, который уже был добавлен в группу +- `create` — Создание кластера. Параметры: [Create params](vc_vdc_group_params_create.md) +- `delete` +- `reconcile` +- `remove_vdc` diff --git a/generated/prod/docs/vc_vdc_group_outputs.md b/generated/prod/docs/vc_vdc_group_outputs.md new file mode 100644 index 0000000..5714cde --- /dev/null +++ b/generated/prod/docs/vc_vdc_group_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vc_vdc_group_params.md b/generated/prod/docs/vc_vdc_group_params.md new file mode 100644 index 0000000..d292ab9 --- /dev/null +++ b/generated/prod/docs/vc_vdc_group_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Parameters + +- [Create params](vc_vdc_group_params_create.md) +- [Modify params](vc_vdc_group_params_modify.md) diff --git a/generated/prod/docs/vc_vdc_group_params_create.md b/generated/prod/docs/vc_vdc_group_params_create.md new file mode 100644 index 0000000..624e982 --- /dev/null +++ b/generated/prod/docs/vc_vdc_group_params_create.md @@ -0,0 +1,20 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
618vdc_uiduuidref_svc_id=21
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/prod/docs/vc_vdc_group_params_modify.md b/generated/prod/docs/vc_vdc_group_params_modify.md new file mode 100644 index 0000000..721de57 --- /dev/null +++ b/generated/prod/docs/vc_vdc_group_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Modify params + +None. diff --git a/generated/prod/docs/vc_vdc_ops.md b/generated/prod/docs/vc_vdc_ops.md new file mode 100644 index 0000000..b1a1776 --- /dev/null +++ b/generated/prod/docs/vc_vdc_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vdc_params_create.md) +- `delete` — Удалить возможно только через 14 дней после выполнения операции `suspend` **ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC** Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vdc_params_modify.md) +- `reconcile` +- `resume` +- `suspend` — Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC diff --git a/generated/prod/docs/vc_vdc_outputs.md b/generated/prod/docs/vc_vdc_outputs.md new file mode 100644 index 0000000..a435292 --- /dev/null +++ b/generated/prod/docs/vc_vdc_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["vdcName"]` + diff --git a/generated/prod/docs/vc_vdc_params.md b/generated/prod/docs/vc_vdc_params.md new file mode 100644 index 0000000..03c0037 --- /dev/null +++ b/generated/prod/docs/vc_vdc_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Parameters + +- [Create params](vc_vdc_params_create.md) +- [Modify params](vc_vdc_params_modify.md) diff --git a/generated/prod/docs/vc_vdc_params_create.md b/generated/prod/docs/vc_vdc_params_create.md new file mode 100644 index 0000000..c9e89a7 --- /dev/null +++ b/generated/prod/docs/vc_vdc_params_create.md @@ -0,0 +1,42 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
30organization_uiduuidДля создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director`ref_svc_id=19
335provider_vdcstringПриписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
361storage_configarray-map-fixedПервая политика в списке указывается как &quot;По умолчанию&quot;
746network_providerstring
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
397cpu_guaranteedinteger20Свойство нельзя изменить после созданияvalue_list=20, 50, 80
398mem_guaranteedinteger20После создания нельзя изменитьvalue_list=20, 50, 80
557cpu_allocatedinteger10штminvalue=1; maxvalue=200
558mem_allocatedinteger20gbminvalue=1; maxvalue=4096
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/vc_vdc_params_modify.md b/generated/prod/docs/vc_vdc_params_modify.md new file mode 100644 index 0000000..4bb0906 --- /dev/null +++ b/generated/prod/docs/vc_vdc_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
560cpu_allocatedintegerштminvalue=1; maxvalue=999
561mem_allocatedintegergbminvalue=1; maxvalue=1024
562storage_configarray-map-fixedПервая политика в списке указывается как &quot;По умолчанию&quot;
diff --git a/generated/prod/docs/vc_vm.md b/generated/prod/docs/vc_vm.md new file mode 100644 index 0000000..e0c84ef --- /dev/null +++ b/generated/prod/docs/vc_vm.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## MAN + +
+Виртуальная машина без каких-либо особенностей.
Данная услуга запускает vc_ip для выдачи белого адреса и подключения к ВМ +
diff --git a/generated/prod/docs/vc_vm_example.md b/generated/prod/docs/vc_vm_example.md new file mode 100644 index 0000000..223d7b0 --- /dev/null +++ b/generated/prod/docs/vc_vm_example.md @@ -0,0 +1,52 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Copy-ready manifest (`vc_vm_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vm" "baza" { + vapp_uid = "TODO" # InstanceUid vApp. vApp должен быть создан + ip_space_name = "TODO" # Брать из свойств ресурсной платформы .vcd.ipSpace Имя IP-space для выделения внешнего IP (IP выделяется обязательно) + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + vm_name = "vm0001" # Имя ВМ. Не поддерживает спец-символы + vm_cpu = 2 # CPU + vm_ram = 2 # RAM (GB) + vm_disksize = 10 # Размер диска под сервис (GB) + template_name = "RockyLinux_9-16G" # ОС + access_ip_list = "79.111.119.78/32 185.247.187.1/32" # Вставить содержимое из 'curl ifconfig.io' IP адреса в CIDR формате через пробел для настройки аксес листов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vm.baza.state_params["имя_ключа"] +nubes_vc_vm.baza.state_out["имя_ключа"] +nubes_vc_vm.baza.state_params_flat["имя_ключа"] +nubes_vc_vm.baza.state_out_flat["имя_ключа"] +nubes_vc_vm.baza.vault_secrets["имя_ключа"] +nubes_vc_vm.baza.vault_url +nubes_vc_vm.baza.vault_user_path +nubes_vc_vm.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_vm_ops.md b/generated/prod/docs/vc_vm_ops.md new file mode 100644 index 0000000..ff1e0cf --- /dev/null +++ b/generated/prod/docs/vc_vm_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vm_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vm_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/vc_vm_outputs.md b/generated/prod/docs/vc_vm_outputs.md new file mode 100644 index 0000000..d5163ba --- /dev/null +++ b/generated/prod/docs/vc_vm_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vc_vm_params.md b/generated/prod/docs/vc_vm_params.md new file mode 100644 index 0000000..0ff6c3e --- /dev/null +++ b/generated/prod/docs/vc_vm_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Parameters + +- [Create params](vc_vm_params_create.md) +- [Modify params](vc_vm_params_modify.md) diff --git a/generated/prod/docs/vc_vm_params_create.md b/generated/prod/docs/vc_vm_params_create.md new file mode 100644 index 0000000..5bed0ba --- /dev/null +++ b/generated/prod/docs/vc_vm_params_create.md @@ -0,0 +1,42 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
33vapp_uiduuidInstanceUid vApp. vApp должен быть созданref_svc_id=26
406ip_space_namestringБрать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно)
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
35vm_namestringvm0001Имя ВМ. Не поддерживает спец-символыregex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
36vm_cpustring2CPU
37vm_ramstring2RAM (GB)
38vm_disksizestring10Размер диска под сервис (GB)
176template_namestringRockyLinux_9-16GОСvalue_list=RockyLinux_9-16G, OracleLinux_9-16G, Ubuntu_24-20G, Ubuntu_22-20G
177access_ip_liststring79.111.119.78/32 185.247.187.1/32IP адреса в CIDR формате через пробел для настройки аксес листов
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/vc_vm_params_modify.md b/generated/prod/docs/vc_vm_params_modify.md new file mode 100644 index 0000000..0ec4505 --- /dev/null +++ b/generated/prod/docs/vc_vm_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
186vm_cpuintegerCPU
187vm_ramintegerRAM
188vm_disksizeintegerРазмер диска под сервис (GB)
189access_ip_liststringIP адреса в CIDR формате через пробел для настройки аксес листов
diff --git a/generated/prod/docs/vc_vm_v2.md b/generated/prod/docs/vc_vm_v2.md new file mode 100644 index 0000000..ada3402 --- /dev/null +++ b/generated/prod/docs/vc_vm_v2.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/prod/docs/vc_vm_v2_example.md b/generated/prod/docs/vc_vm_v2_example.md new file mode 100644 index 0000000..a22d7c6 --- /dev/null +++ b/generated/prod/docs/vc_vm_v2_example.md @@ -0,0 +1,45 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Copy-ready manifest (`vc_vm_v2_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vm_v2" "baza" { + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + server_list_json = [{}] # Необходимо изменить vappUid и ipSpaceName Пример конфигурации взять по ссылке ниже Объект серверов вида [{}] (Map(Object)) Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vm_v2.baza.state_params["имя_ключа"] +nubes_vc_vm_v2.baza.state_out["имя_ключа"] +nubes_vc_vm_v2.baza.state_params_flat["имя_ключа"] +nubes_vc_vm_v2.baza.state_out_flat["имя_ключа"] +nubes_vc_vm_v2.baza.vault_secrets["имя_ключа"] +nubes_vc_vm_v2.baza.vault_url +nubes_vc_vm_v2.baza.vault_user_path +nubes_vc_vm_v2.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_vm_v2_ops.md b/generated/prod/docs/vc_vm_v2_ops.md new file mode 100644 index 0000000..9d940af --- /dev/null +++ b/generated/prod/docs/vc_vm_v2_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vm_v2_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vm_v2_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/prod/docs/vc_vm_v2_outputs.md b/generated/prod/docs/vc_vm_v2_outputs.md new file mode 100644 index 0000000..3b9bace --- /dev/null +++ b/generated/prod/docs/vc_vm_v2_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vc_vm_v2_params.md b/generated/prod/docs/vc_vm_v2_params.md new file mode 100644 index 0000000..e60c762 --- /dev/null +++ b/generated/prod/docs/vc_vm_v2_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Parameters + +- [Create params](vc_vm_v2_params_create.md) +- [Modify params](vc_vm_v2_params_modify.md) diff --git a/generated/prod/docs/vc_vm_v2_params_create.md b/generated/prod/docs/vc_vm_v2_params_create.md new file mode 100644 index 0000000..b1c20cc --- /dev/null +++ b/generated/prod/docs/vc_vm_v2_params_create.md @@ -0,0 +1,32 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + +None. + +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
301server_list_jsonjson[{}]Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)
maxlength=65536; minlength=10
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/vc_vm_v2_params_modify.md b/generated/prod/docs/vc_vm_v2_params_modify.md new file mode 100644 index 0000000..eedbec8 --- /dev/null +++ b/generated/prod/docs/vc_vm_v2_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
302server_list_jsonjsonОбъект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)
maxlength=65536; minlength=10
diff --git a/generated/prod/docs/vc_vm_v3.md b/generated/prod/docs/vc_vm_v3.md new file mode 100644 index 0000000..95856d8 --- /dev/null +++ b/generated/prod/docs/vc_vm_v3.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## MAN + +
+# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики +
diff --git a/generated/prod/docs/vc_vm_v3_example.md b/generated/prod/docs/vc_vm_v3_example.md new file mode 100644 index 0000000..af8e218 --- /dev/null +++ b/generated/prod/docs/vc_vm_v3_example.md @@ -0,0 +1,53 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Copy-ready manifest (`vc_vm_v3_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vc_vm_v3" "baza" { + vapp_uid = "TODO" + ip_space_name = "TODO" + image_vm = "TODO" + user_public_key = "TODO" # Публичная часть SSH-Ключа для пользователя + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + vm_name = "web01" + vm_cpu = 1 # шт + vm_ram = 1 # Gb + user_login = "myuser" # Учётка SSH + access_port_list = ["22"] # По каким портам открыть доступ к ВМ. Порты 22, 10050 проставляются автоматически Пока доступно: 22 - SSH, 80 - HTTP, 443 - HTTPS, 10050 - ZABBIX +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vm_v3.baza.state_params["имя_ключа"] +nubes_vc_vm_v3.baza.state_out["имя_ключа"] +nubes_vc_vm_v3.baza.state_params_flat["имя_ключа"] +nubes_vc_vm_v3.baza.state_out_flat["имя_ключа"] +nubes_vc_vm_v3.baza.vault_secrets["имя_ключа"] +nubes_vc_vm_v3.baza.vault_url +nubes_vc_vm_v3.baza.vault_user_path +nubes_vc_vm_v3.baza.vault_fields +``` diff --git a/generated/prod/docs/vc_vm_v3_ops.md b/generated/prod/docs/vc_vm_v3_ops.md new file mode 100644 index 0000000..29b88fb --- /dev/null +++ b/generated/prod/docs/vc_vm_v3_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vm_v3_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vm_v3_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/prod/docs/vc_vm_v3_outputs.md b/generated/prod/docs/vc_vm_v3_outputs.md new file mode 100644 index 0000000..d0ef2e1 --- /dev/null +++ b/generated/prod/docs/vc_vm_v3_outputs.md @@ -0,0 +1,30 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["fqdn"]` +- `state_out_flat["internalConnect"]` + diff --git a/generated/prod/docs/vc_vm_v3_params.md b/generated/prod/docs/vc_vm_v3_params.md new file mode 100644 index 0000000..9c63ad1 --- /dev/null +++ b/generated/prod/docs/vc_vm_v3_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Parameters + +- [Create params](vc_vm_v3_params_create.md) +- [Modify params](vc_vm_v3_params_modify.md) diff --git a/generated/prod/docs/vc_vm_v3_params_create.md b/generated/prod/docs/vc_vm_v3_params_create.md new file mode 100644 index 0000000..aff77f9 --- /dev/null +++ b/generated/prod/docs/vc_vm_v3_params_create.md @@ -0,0 +1,46 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
407vapp_uiduuidref_svc_id=26
411vm_diskintegerОсновной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму
regex=^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$
412ip_space_namestring
413access_ip_listjsonФормат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено [&quot;0.0.0.0/0&quot;]
regex=^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$
414image_vmstring
415cloud_inityamlМожно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов.
417user_public_keystringПубличная часть SSH-Ключа для пользователя
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
408vm_namestringweb01regex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
409vm_cpuinteger1штminvalue=1; maxvalue=64
410vm_raminteger1Gbminvalue=1; maxvalue=256
416user_loginmyuserУчётка SSH
448access_port_liststring["22"]По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/prod/docs/vc_vm_v3_params_modify.md b/generated/prod/docs/vc_vm_v3_params_modify.md new file mode 100644 index 0000000..033040d --- /dev/null +++ b/generated/prod/docs/vc_vm_v3_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
493vm_cpuintegerштminvalue=1; maxvalue=64
494vm_ramintegerGbminvalue=1; maxvalue=256
495vm_diskintegerОсновной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму
regex=^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$
496ip_space_namestringno-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev
value_list=no-needed, internet-antiddos-v1, internet-no-antiddos-v1
497access_ip_listjsonФормат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено [&quot;0.0.0.0/0&quot;]
498access_port_listjsonФормат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS
diff --git a/generated/prod/docs/vcexternalip.md b/generated/prod/docs/vcexternalip.md new file mode 100644 index 0000000..3fe625c --- /dev/null +++ b/generated/prod/docs/vcexternalip.md @@ -0,0 +1,13 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## MAN + +
+**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса. +
diff --git a/generated/prod/docs/vcexternalip_example.md b/generated/prod/docs/vcexternalip_example.md new file mode 100644 index 0000000..8d53544 --- /dev/null +++ b/generated/prod/docs/vcexternalip_example.md @@ -0,0 +1,51 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Copy-ready manifest (`vcexternalip_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vcexternalip" "baza" { + service_uid = "TODO" + internal_addr_access = "TODO" # Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true + from_service_namespace = "TODO" # Namespace составной услуги (для отображения правил FW) + ip_space_name = "TODO" # Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + dnat_create = true # Необходимо создать DNAT-правило. + internal_port_access = 5432 # Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vcexternalip.baza.state_params["имя_ключа"] +nubes_vcexternalip.baza.state_out["имя_ключа"] +nubes_vcexternalip.baza.state_params_flat["имя_ключа"] +nubes_vcexternalip.baza.state_out_flat["имя_ключа"] +nubes_vcexternalip.baza.vault_secrets["имя_ключа"] +nubes_vcexternalip.baza.vault_url +nubes_vcexternalip.baza.vault_user_path +nubes_vcexternalip.baza.vault_fields +``` diff --git a/generated/prod/docs/vcexternalip_ops.md b/generated/prod/docs/vcexternalip_ops.md new file mode 100644 index 0000000..5668481 --- /dev/null +++ b/generated/prod/docs/vcexternalip_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vcexternalip_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vcexternalip_params_modify.md) diff --git a/generated/prod/docs/vcexternalip_outputs.md b/generated/prod/docs/vcexternalip_outputs.md new file mode 100644 index 0000000..aaa59e1 --- /dev/null +++ b/generated/prod/docs/vcexternalip_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalIp"]` + diff --git a/generated/prod/docs/vcexternalip_params.md b/generated/prod/docs/vcexternalip_params.md new file mode 100644 index 0000000..1db2fcc --- /dev/null +++ b/generated/prod/docs/vcexternalip_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Parameters + +- [Create params](vcexternalip_params_create.md) +- [Modify params](vcexternalip_params_modify.md) diff --git a/generated/prod/docs/vcexternalip_params_create.md b/generated/prod/docs/vcexternalip_params_create.md new file mode 100644 index 0000000..995a3b8 --- /dev/null +++ b/generated/prod/docs/vcexternalip_params_create.md @@ -0,0 +1,36 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
140service_uidstring
143snat_createbooleanНеобходимо создать SNAT-правило.
144internal_addr_accessstringВнутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true
324from_service_namespacestringNamespace составной услуги (для отображения правил FW)
325from_service_cloud_edge_namestringEdge составной услуги (указывать, если resourceRealm связан с CloudDirector)
326from_service_cloud_vdc_nameVdc составной услуги (указывать, если resourceRealm связан с CloudDirector)
327from_service_cloud_org_namestringOrg составной услуги (указывать, если resourceRealm связан с CloudDirector)
328from_service_cloud_vmware_urlstringApi-Url составной услуги (указывать, если resourceRealm связан с CloudDirector)
336ip_space_namestringТип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)
488resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
624from_service_cloud_edge_scopestring
626from_service_vdc_group_namestring
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
141dnat_createbooleantrueНеобходимо создать DNAT-правило.
142internal_port_accessstring5432Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
diff --git a/generated/prod/docs/vcexternalip_params_modify.md b/generated/prod/docs/vcexternalip_params_modify.md new file mode 100644 index 0000000..f17b66c --- /dev/null +++ b/generated/prod/docs/vcexternalip_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
627service_uidstring
628ip_space_namestring
629dnat_createboolean
630snat_createboolean
631internal_port_accessstring
632internal_addr_accessstring
diff --git a/generated/prod/docs/vmpostgre.md b/generated/prod/docs/vmpostgre.md new file mode 100644 index 0000000..31d8dad --- /dev/null +++ b/generated/prod/docs/vmpostgre.md @@ -0,0 +1,13 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## MAN + +
+Сервис настройки PostgreSQL для виртуальной машины.
Требует наличия ВМ +
diff --git a/generated/prod/docs/vmpostgre_example.md b/generated/prod/docs/vmpostgre_example.md new file mode 100644 index 0000000..cebe6a2 --- /dev/null +++ b/generated/prod/docs/vmpostgre_example.md @@ -0,0 +1,51 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Copy-ready manifest (`vmpostgre_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "2.1.26" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://deck-api.ngcloud.ru/api/v1/index.cfm" +} +resource "nubes_vmpostgre" "baza" { + vdc_uid = "TODO" # InstanceUid vdc. Vdc должен быть создан + nsxt_uid = "TODO" # InstanceUid nsxt. Nsxt должен быть создан + vm_uid = "TODO" # InstanceUid ВМ. ВМ должна быть создана + resource_realm = "TODO" # Ресурсная платформа + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + user_db = "servicedb" # Наименование Базы данных + user_login = "serviceuser" # Пользователь базы данных + user_password = "fj29gm48fm!d374mc9" # Пароль от доступа к Базе данных +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vmpostgre.baza.state_params["имя_ключа"] +nubes_vmpostgre.baza.state_out["имя_ключа"] +nubes_vmpostgre.baza.state_params_flat["имя_ключа"] +nubes_vmpostgre.baza.state_out_flat["имя_ключа"] +nubes_vmpostgre.baza.vault_secrets["имя_ключа"] +nubes_vmpostgre.baza.vault_url +nubes_vmpostgre.baza.vault_user_path +nubes_vmpostgre.baza.vault_fields +``` diff --git a/generated/prod/docs/vmpostgre_ops.md b/generated/prod/docs/vmpostgre_ops.md new file mode 100644 index 0000000..306580c --- /dev/null +++ b/generated/prod/docs/vmpostgre_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vmpostgre_params_create.md) +- `delete` diff --git a/generated/prod/docs/vmpostgre_outputs.md b/generated/prod/docs/vmpostgre_outputs.md new file mode 100644 index 0000000..5c8d505 --- /dev/null +++ b/generated/prod/docs/vmpostgre_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/prod/docs/vmpostgre_params.md b/generated/prod/docs/vmpostgre_params.md new file mode 100644 index 0000000..8b28da6 --- /dev/null +++ b/generated/prod/docs/vmpostgre_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Parameters + +- [Create params](vmpostgre_params_create.md) +- [Modify params](vmpostgre_params_modify.md) diff --git a/generated/prod/docs/vmpostgre_params_create.md b/generated/prod/docs/vmpostgre_params_create.md new file mode 100644 index 0000000..9d0a40e --- /dev/null +++ b/generated/prod/docs/vmpostgre_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
43vdc_uiduuidInstanceUid vdc. Vdc должен быть создан
ref_svc_id=21
44nsxt_uiduuidInstanceUid nsxt. Nsxt должен быть создан
ref_svc_id=22
45vm_uiduuidInstanceUid ВМ. ВМ должна быть создана
ref_svc_id=23
859resource_realmstringРесурсная платформаfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
46user_dbstringservicedbНаименование Базы данных
47user_loginstringserviceuserПользователь базы данных
48user_passwordstringfj29gm48fm!d374mc9Пароль от доступа к Базе данных
diff --git a/generated/prod/docs/vmpostgre_params_modify.md b/generated/prod/docs/vmpostgre_params_modify.md new file mode 100644 index 0000000..9ce30ad --- /dev/null +++ b/generated/prod/docs/vmpostgre_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Modify params + +None. diff --git a/generated/prod/go/.gitkeep b/generated/prod/go/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/prod/go/100_openwhisk_resource.go b/generated/prod/go/100_openwhisk_resource.go new file mode 100644 index 0000000..a389f20 --- /dev/null +++ b/generated/prod/go/100_openwhisk_resource.go @@ -0,0 +1,352 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: openwhisk +// Service ID: 100 + +var _ resource.Resource = &OpenwhiskResource{} +var _ resource.ResourceWithModifyPlan = &OpenwhiskResource{} +var _ resource.ResourceWithImportState = &OpenwhiskResource{} + +type OpenwhiskResource struct { + client *core.UniversalClient +} + +type OpenwhiskModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ResourceRealm types.String `tfsdk:"resource_realm"` + Username types.String `tfsdk:"username"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewOpenwhiskResource() resource.Resource { + return &OpenwhiskResource{} +} + +func (r *OpenwhiskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_openwhisk" +} + +func (r *OpenwhiskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Ресурсная платформа Не изменяется после создания."}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Имя пользователя Openwhisk Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *OpenwhiskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *OpenwhiskModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *OpenwhiskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan OpenwhiskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Username.IsNull() && !plan.Username.IsUnknown() && !state.Username.IsNull() && !state.Username.IsUnknown() { + if plan.Username.ValueString() != state.Username.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить username", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.Username.IsNull() || config.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 64: resources_core.FormatString(config.Username), + 863: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 100, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *OpenwhiskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data OpenwhiskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.Username.IsNull() || data.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 64: resources_core.FormatString(data.Username), + 863: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 100, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "username", Field: "Username", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *OpenwhiskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state OpenwhiskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 100, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "username", Field: "Username", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *OpenwhiskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan OpenwhiskModel + var state OpenwhiskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 100, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "username", Field: "Username", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *OpenwhiskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state OpenwhiskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *OpenwhiskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *OpenwhiskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/110_dnszone_resource.go b/generated/prod/go/110_dnszone_resource.go new file mode 100644 index 0000000..c0b9b1a --- /dev/null +++ b/generated/prod/go/110_dnszone_resource.go @@ -0,0 +1,452 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: dnszone +// Service ID: 110 + +var _ resource.Resource = &DnszoneResource{} +var _ resource.ResourceWithModifyPlan = &DnszoneResource{} +var _ resource.ResourceWithImportState = &DnszoneResource{} + +type DnszoneResource struct { + client *core.UniversalClient +} + +type DnszoneModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ResourceRealm types.String `tfsdk:"resource_realm"` + ZoneEmail types.String `tfsdk:"zone_email"` + ZoneKind types.String `tfsdk:"zone_kind"` + ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` + ZoneName types.String `tfsdk:"zone_name"` + ZoneTimeToExpire types.String `tfsdk:"zone_time_to_expire"` + ZoneTimeToLive types.String `tfsdk:"zone_time_to_live"` + ZoneTimeToRefresh types.String `tfsdk:"zone_time_to_refresh"` + ZoneTimeToRetry types.String `tfsdk:"zone_time_to_retry"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewDnszoneResource() resource.Resource { + return &DnszoneResource{} +} + +func (r *DnszoneResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dnszone" +} + +func (r *DnszoneResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "zone_email": schema.StringAttribute{Required: true, MarkdownDescription: "Контактный e-mail Не изменяется после создания."}, + "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, + "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, + "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, + "zone_time_to_expire": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3600000"), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, + "zone_time_to_live": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, + "zone_time_to_refresh": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, + "zone_time_to_retry": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("7200"), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *DnszoneResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *DnszoneModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *DnszoneModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan DnszoneModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneEmail.IsNull() && !plan.ZoneEmail.IsUnknown() && !state.ZoneEmail.IsNull() && !state.ZoneEmail.IsUnknown() { + if plan.ZoneEmail.ValueString() != state.ZoneEmail.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_email", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { + if plan.ZoneKind.ValueString() != state.ZoneKind.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { + if plan.ZoneMastersForSlave.ValueString() != state.ZoneMastersForSlave.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { + if plan.ZoneName.ValueString() != state.ZoneName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToExpire.IsNull() && !plan.ZoneTimeToExpire.IsUnknown() && !state.ZoneTimeToExpire.IsNull() && !state.ZoneTimeToExpire.IsUnknown() { + if plan.ZoneTimeToExpire.ValueString() != state.ZoneTimeToExpire.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_expire", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToLive.IsNull() && !plan.ZoneTimeToLive.IsUnknown() && !state.ZoneTimeToLive.IsNull() && !state.ZoneTimeToLive.IsUnknown() { + if plan.ZoneTimeToLive.ValueString() != state.ZoneTimeToLive.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_live", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToRefresh.IsNull() && !plan.ZoneTimeToRefresh.IsUnknown() && !state.ZoneTimeToRefresh.IsNull() && !state.ZoneTimeToRefresh.IsUnknown() { + if plan.ZoneTimeToRefresh.ValueString() != state.ZoneTimeToRefresh.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_refresh", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneTimeToRetry.IsNull() && !plan.ZoneTimeToRetry.IsUnknown() && !state.ZoneTimeToRetry.IsNull() && !state.ZoneTimeToRetry.IsUnknown() { + if plan.ZoneTimeToRetry.ValueString() != state.ZoneTimeToRetry.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_time_to_retry", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ZoneEmail.IsNull() || config.ZoneEmail.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") + return + } + if config.ZoneName.IsNull() || config.ZoneName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 66: resources_core.FormatString(config.ZoneEmail), + 67: resources_core.FormatString(config.ZoneName), + 68: resources_core.FormatString(config.ZoneKind), + 69: resources_core.FormatString(config.ZoneMastersForSlave), + 70: resources_core.FormatString(config.ZoneTimeToRefresh), + 71: resources_core.FormatString(config.ZoneTimeToRetry), + 72: resources_core.FormatString(config.ZoneTimeToExpire), + 73: resources_core.FormatString(config.ZoneTimeToLive), + 284: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 110, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *DnszoneResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data DnszoneModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.ZoneEmail.IsNull() || data.ZoneEmail.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") + return + } + if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 66: resources_core.FormatString(data.ZoneEmail), + 67: resources_core.FormatString(data.ZoneName), + 68: resources_core.FormatString(data.ZoneKind), + 69: resources_core.FormatString(data.ZoneMastersForSlave), + 70: resources_core.FormatString(data.ZoneTimeToRefresh), + 71: resources_core.FormatString(data.ZoneTimeToRetry), + 72: resources_core.FormatString(data.ZoneTimeToExpire), + 73: resources_core.FormatString(data.ZoneTimeToLive), + 284: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 110, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnszoneResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DnszoneModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 110, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan DnszoneModel + var state DnszoneModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 110, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, + {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, + {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, + {Code: "zoneName", Field: "ZoneName", Type: "string"}, + {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, + {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, + {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, + {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnszoneResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state DnszoneModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *DnszoneResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *DnszoneResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/111_dnsrecord_resource.go b/generated/prod/go/111_dnsrecord_resource.go new file mode 100644 index 0000000..c52b61d --- /dev/null +++ b/generated/prod/go/111_dnsrecord_resource.go @@ -0,0 +1,471 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: dnsrecord +// Service ID: 111 + +var _ resource.Resource = &DnsrecordResource{} +var _ resource.ResourceWithModifyPlan = &DnsrecordResource{} +var _ resource.ResourceWithImportState = &DnsrecordResource{} + +type DnsrecordResource struct { + client *core.UniversalClient +} + +type DnsrecordModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + RecordInput types.String `tfsdk:"record_input"` + RecordName types.String `tfsdk:"record_name"` + RecordTTL types.Int64 `tfsdk:"record_t_t_l"` + RecordType types.String `tfsdk:"record_type"` + ZoneUid types.String `tfsdk:"zone_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewDnsrecordResource() resource.Resource { + return &DnsrecordResource{} +} + +func (r *DnsrecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dnsrecord" +} + +func (r *DnsrecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP"}, + "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя записи. Имя зоны в конце проставляется автоматически Не изменяется после создания."}, + "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена Не изменяется после создания."}, + "record_type": schema.StringAttribute{Required: true, MarkdownDescription: "Тип DNS-записи Не изменяется после создания."}, + "zone_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID экземпляра услуги DNS-зоны Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *DnsrecordResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *DnsrecordModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *DnsrecordModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan DnsrecordModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, plan.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != plan.ZoneUid.ValueString() { + plan.ZoneUid = types.StringValue(resolvedZoneUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.RecordName.IsNull() && !plan.RecordName.IsUnknown() && !state.RecordName.IsNull() && !state.RecordName.IsUnknown() { + if plan.RecordName.ValueString() != state.RecordName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить record_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RecordTTL.IsNull() && !plan.RecordTTL.IsUnknown() && !state.RecordTTL.IsNull() && !state.RecordTTL.IsUnknown() { + if plan.RecordTTL.ValueInt64() != state.RecordTTL.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить record_t_t_l", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RecordType.IsNull() && !plan.RecordType.IsUnknown() && !state.RecordType.IsNull() && !state.RecordType.IsUnknown() { + if plan.RecordType.ValueString() != state.RecordType.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить record_type", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { + if plan.ZoneUid.ValueString() != state.ZoneUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить zone_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.RecordInput.IsNull() || config.RecordInput.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") + return + } + if config.RecordName.IsNull() || config.RecordName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") + return + } + if config.RecordType.IsNull() || config.RecordType.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") + return + } + if config.ZoneUid.IsNull() || config.ZoneUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.ZoneUid.IsNull() && !config.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, config.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != config.ZoneUid.ValueString() { + config.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + params := map[int]string{ + 75: resources_core.FormatString(config.ZoneUid), + 76: resources_core.FormatString(config.RecordType), + 77: resources_core.FormatString(config.RecordName), + 78: resources_core.FormatString(config.RecordInput), + 450: resources_core.FormatInt64(config.RecordTTL), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 111, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *DnsrecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data DnsrecordModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.RecordInput.IsNull() || data.RecordInput.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") + return + } + if data.RecordName.IsNull() || data.RecordName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") + return + } + if data.RecordType.IsNull() || data.RecordType.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") + return + } + if data.ZoneUid.IsNull() || data.ZoneUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") + return + } + if !data.ZoneUid.IsNull() && !data.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, data.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedZoneUid != "" && resolvedZoneUid != data.ZoneUid.ValueString() { + data.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 75: resources_core.FormatString(data.ZoneUid), + 76: resources_core.FormatString(data.RecordType), + 77: resources_core.FormatString(data.RecordName), + 78: resources_core.FormatString(data.RecordInput), + 450: resources_core.FormatInt64(data.RecordTTL), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 111, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "recordInput", Field: "RecordInput", Type: "string"}, + {Code: "recordName", Field: "RecordName", Type: "string"}, + {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, + {Code: "recordType", Field: "RecordType", Type: "string"}, + {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, state.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { + state.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnsrecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DnsrecordModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 111, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "recordInput", Field: "RecordInput", Type: "string"}, + {Code: "recordName", Field: "RecordName", Type: "string"}, + {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, + {Code: "recordType", Field: "RecordType", Type: "string"}, + {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, newState.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != newState.ZoneUid.ValueString() { + newState.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan DnsrecordModel + var state DnsrecordModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.RecordInput.IsNull() != state.RecordInput.IsNull() || plan.RecordInput.IsUnknown() != state.RecordInput.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.RecordInput.IsNull() && !plan.RecordInput.IsUnknown() && !state.RecordInput.IsNull() && !state.RecordInput.IsUnknown() { + if plan.RecordInput.ValueString() != state.RecordInput.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 256: resources_core.FormatString(plan.RecordInput), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 111, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "recordInput", Field: "RecordInput", Type: "string"}, + {Code: "recordName", Field: "RecordName", Type: "string"}, + {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, + {Code: "recordType", Field: "RecordType", Type: "string"}, + {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { + resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 110, state.ZoneUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) + } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { + state.ZoneUid = types.StringValue(resolvedZoneUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DnsrecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state DnsrecordModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *DnsrecordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *DnsrecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/112_tenant_resource.go b/generated/prod/go/112_tenant_resource.go new file mode 100644 index 0000000..bea2f79 --- /dev/null +++ b/generated/prod/go/112_tenant_resource.go @@ -0,0 +1,352 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: tenant +// Service ID: 112 + +var _ resource.Resource = &TenantResource{} +var _ resource.ResourceWithModifyPlan = &TenantResource{} +var _ resource.ResourceWithImportState = &TenantResource{} + +type TenantResource struct { + client *core.UniversalClient +} + +type TenantModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + GrafanaOrgName types.String `tfsdk:"grafana_org_name"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewTenantResource() resource.Resource { + return &TenantResource{} +} + +func (r *TenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_tenant" +} + +func (r *TenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "grafana_org_name": schema.StringAttribute{Required: true, MarkdownDescription: "WZ-Код клиента Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *TenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *TenantModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *TenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan TenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.GrafanaOrgName.IsNull() && !plan.GrafanaOrgName.IsUnknown() && !state.GrafanaOrgName.IsNull() && !state.GrafanaOrgName.IsUnknown() { + if plan.GrafanaOrgName.ValueString() != state.GrafanaOrgName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить grafana_org_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.GrafanaOrgName.IsNull() || config.GrafanaOrgName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 239: resources_core.FormatString(config.GrafanaOrgName), + 240: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 112, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data TenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.GrafanaOrgName.IsNull() || data.GrafanaOrgName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 239: resources_core.FormatString(data.GrafanaOrgName), + 240: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 112, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *TenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state TenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 112, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan TenantModel + var state TenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 112, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *TenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state TenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *TenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *TenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/113_vc_complex_resource.go b/generated/prod/go/113_vc_complex_resource.go new file mode 100644 index 0000000..b3e5bd1 --- /dev/null +++ b/generated/prod/go/113_vc_complex_resource.go @@ -0,0 +1,523 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_complex +// Service ID: 113 + +var _ resource.Resource = &VcComplexResource{} +var _ resource.ResourceWithModifyPlan = &VcComplexResource{} +var _ resource.ResourceWithImportState = &VcComplexResource{} + +type VcComplexResource struct { + client *core.UniversalClient +} + +type VcComplexModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + CpuAllocated types.String `tfsdk:"cpu_allocated"` + CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + MemAllocated types.Int64 `tfsdk:"mem_allocated"` + MemGuaranteed types.Int64 `tfsdk:"mem_guaranteed"` + NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` + NeedExternalAddressSNAT types.Bool `tfsdk:"need_external_address_s_n_a_t"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SegroupName types.String `tfsdk:"segroup_name"` + StorageConfig types.String `tfsdk:"storage_config"` + VIPCount types.Int64 `tfsdk:"v_i_p_count"` + VappName types.String `tfsdk:"vapp_name"` + VdcNetworkPool types.String `tfsdk:"vdc_network_pool"` + VdcProviderGateway types.String `tfsdk:"vdc_provider_gateway"` + VirtualServicesCount types.String `tfsdk:"virtual_services_count"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcComplexResource() resource.Resource { + return &VcComplexResource{} +} + +func (r *VcComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_complex" +} + +func (r *VcComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "cpu_allocated": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "шт Не изменяется после создания."}, + "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, + "ip_space_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("internet-ipv4-v1"), MarkdownDescription: "Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT Не изменяется после создания."}, + "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "gb Не изменяется после создания."}, + "mem_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, + "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает AVI в Cloud Director Не изменяется после создания."}, + "need_external_address_s_n_a_t": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP на Edge. Используется для SNAT всего трафика Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "segroup_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("SEGROUP-SANDBOX-CL1-SHARED-01"), MarkdownDescription: "Наименование segroup, в котором будут выделяться пулы на AVI Не изменяется после создания."}, + "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{\"name\": \"\", \"size\": 1024}]"), MarkdownDescription: "Описывается как массив объектов, где описывается имя политики и её размер Не изменяется после создания."}, + "v_i_p_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "ш Не изменяется после создания."}, + "vapp_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя vApp Не изменяется после создания."}, + "vdc_network_pool": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nsxt-sandbox-geneve-np"), MarkdownDescription: "Не изменяется после создания."}, + "vdc_provider_gateway": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox-v1cl1-pvdc"), MarkdownDescription: "Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String Не изменяется после создания."}, + "virtual_services_count": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcComplexModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcComplexModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcComplexModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { + if plan.CpuAllocated.ValueString() != state.CpuAllocated.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить cpu_allocated", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { + if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { + if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить mem_allocated", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.MemGuaranteed.IsNull() && !plan.MemGuaranteed.IsUnknown() && !state.MemGuaranteed.IsNull() && !state.MemGuaranteed.IsUnknown() { + if plan.MemGuaranteed.ValueInt64() != state.MemGuaranteed.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить mem_guaranteed", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { + if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_enable_a_v_i", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressSNAT.IsNull() && !plan.NeedExternalAddressSNAT.IsUnknown() && !state.NeedExternalAddressSNAT.IsNull() && !state.NeedExternalAddressSNAT.IsUnknown() { + if plan.NeedExternalAddressSNAT.ValueBool() != state.NeedExternalAddressSNAT.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_s_n_a_t", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.SegroupName.IsNull() && !plan.SegroupName.IsUnknown() && !state.SegroupName.IsNull() && !state.SegroupName.IsUnknown() { + if plan.SegroupName.ValueString() != state.SegroupName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить segroup_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { + if plan.StorageConfig.ValueString() != state.StorageConfig.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить storage_config", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VIPCount.IsNull() && !plan.VIPCount.IsUnknown() && !state.VIPCount.IsNull() && !state.VIPCount.IsUnknown() { + if plan.VIPCount.ValueInt64() != state.VIPCount.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить v_i_p_count", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { + if plan.VappName.ValueString() != state.VappName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcNetworkPool.IsNull() && !plan.VdcNetworkPool.IsUnknown() && !state.VdcNetworkPool.IsNull() && !state.VdcNetworkPool.IsUnknown() { + if plan.VdcNetworkPool.ValueString() != state.VdcNetworkPool.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_network_pool", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcProviderGateway.IsNull() && !plan.VdcProviderGateway.IsUnknown() && !state.VdcProviderGateway.IsNull() && !state.VdcProviderGateway.IsUnknown() { + if plan.VdcProviderGateway.ValueString() != state.VdcProviderGateway.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_provider_gateway", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { + if plan.VirtualServicesCount.ValueString() != state.VirtualServicesCount.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить virtual_services_count", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.VappName.IsNull() || config.VappName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 332: resources_core.FormatString(config.ResourceRealm), + 334: resources_core.FormatString(config.VappName), + 358: resources_core.FormatString(config.VdcProviderGateway), + 359: resources_core.FormatString(config.VirtualServicesCount), + 360: resources_core.FormatString(config.IpSpaceName), + 399: resources_core.FormatString(config.StorageConfig), + 400: resources_core.FormatString(config.VdcNetworkPool), + 401: resources_core.FormatInt64(config.CpuGuaranteed), + 402: resources_core.FormatInt64(config.MemGuaranteed), + 403: resources_core.FormatString(config.SegroupName), + 404: resources_core.FormatBool(config.NeedExternalAddressSNAT), + 405: resources_core.FormatBool(config.NeedEnableAVI), + 566: resources_core.FormatString(config.CpuAllocated), + 567: resources_core.FormatInt64(config.MemAllocated), + 568: resources_core.FormatInt64(config.VIPCount), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 113, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcComplexModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.VappName.IsNull() || data.VappName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 332: resources_core.FormatString(data.ResourceRealm), + 334: resources_core.FormatString(data.VappName), + 358: resources_core.FormatString(data.VdcProviderGateway), + 359: resources_core.FormatString(data.VirtualServicesCount), + 360: resources_core.FormatString(data.IpSpaceName), + 399: resources_core.FormatString(data.StorageConfig), + 400: resources_core.FormatString(data.VdcNetworkPool), + 401: resources_core.FormatInt64(data.CpuGuaranteed), + 402: resources_core.FormatInt64(data.MemGuaranteed), + 403: resources_core.FormatString(data.SegroupName), + 404: resources_core.FormatBool(data.NeedExternalAddressSNAT), + 405: resources_core.FormatBool(data.NeedEnableAVI), + 566: resources_core.FormatString(data.CpuAllocated), + 567: resources_core.FormatInt64(data.MemAllocated), + 568: resources_core.FormatInt64(data.VIPCount), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 113, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "segroupName", Field: "SegroupName", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, + {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcComplexModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 113, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "segroupName", Field: "SegroupName", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, + {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcComplexModel + var state VcComplexModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 113, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "segroupName", Field: "SegroupName", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, + {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcComplexModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/114_gitea_complex_resource.go b/generated/prod/go/114_gitea_complex_resource.go new file mode 100644 index 0000000..a1320ad --- /dev/null +++ b/generated/prod/go/114_gitea_complex_resource.go @@ -0,0 +1,305 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: gitea_complex +// Service ID: 114 + +var _ resource.Resource = &GiteaComplexResource{} +var _ resource.ResourceWithModifyPlan = &GiteaComplexResource{} +var _ resource.ResourceWithImportState = &GiteaComplexResource{} + +type GiteaComplexResource struct { + client *core.UniversalClient +} + +type GiteaComplexModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewGiteaComplexResource() resource.Resource { + return &GiteaComplexResource{} +} + +func (r *GiteaComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_gitea_complex" +} + +func (r *GiteaComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *GiteaComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *GiteaComplexModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *GiteaComplexModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan GiteaComplexModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{} + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 114, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *GiteaComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data GiteaComplexModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 114, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{} + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 114, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 114, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{}) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *GiteaComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state GiteaComplexModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 114, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{}) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *GiteaComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan GiteaComplexModel + var state GiteaComplexModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 114, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{}) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *GiteaComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state GiteaComplexModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *GiteaComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *GiteaComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/115_mariadb_resource.go b/generated/prod/go/115_mariadb_resource.go new file mode 100644 index 0000000..11f1547 --- /dev/null +++ b/generated/prod/go/115_mariadb_resource.go @@ -0,0 +1,579 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mariadb +// Service ID: 115 + +var _ resource.Resource = &MariadbResource{} +var _ resource.ResourceWithModifyPlan = &MariadbResource{} +var _ resource.ResourceWithImportState = &MariadbResource{} + +type MariadbResource struct { + client *core.UniversalClient +} + +type MariadbModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AppVersion types.String `tfsdk:"app_version"` + AutoScale types.Bool `tfsdk:"auto_scale"` + AutoScalePercentage types.Int64 `tfsdk:"auto_scale_percentage"` + AutoScaleQuotaGb types.Int64 `tfsdk:"auto_scale_quota_gb"` + AutoScaleTechWindow types.Int64 `tfsdk:"auto_scale_tech_window"` + ExtBACKUPSCHEDULE types.String `tfsdk:"ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewMariadbResource() resource.Resource { + return &MariadbResource{} +} + +func (r *MariadbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mariadb" +} + +func (r *MariadbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9.4.0"), MarkdownDescription: "Версия MySQL Не изменяется после создания."}, + "auto_scale": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, + "auto_scale_percentage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема"}, + "auto_scale_quota_gb": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота для autoScale"}, + "auto_scale_tech_window": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, + "ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 * * * *"), MarkdownDescription: "Не обязательный. Время запуска бекапа"}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний (белый) IP для доступа к master"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores)"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MariadbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *MariadbModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *MariadbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan MariadbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить app_version", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 422: resources_core.FormatString(config.ResourceRealm), + 424: resources_core.FormatInt64(config.ResourceCPU), + 425: resources_core.FormatInt64(config.ResourceMemory), + 426: resources_core.FormatInt64(config.ResourceDisk), + 427: resources_core.FormatInt64(config.ResourceInstances), + 428: resources_core.FormatBool(config.NeedExternalAddressMaster), + 429: resources_core.FormatString(config.IpSpaceNameMaster), + 430: resources_core.FormatString(config.ExtBACKUPSCHEDULE), + 431: resources_core.FormatString(config.AppVersion), + 432: resources_core.FormatBool(config.AutoScale), + 433: resources_core.FormatInt64(config.AutoScalePercentage), + 434: resources_core.FormatInt64(config.AutoScaleTechWindow), + 435: resources_core.FormatInt64(config.AutoScaleQuotaGb), + 459: resources_core.FormatString(config.S3Uid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 115, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *MariadbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data MariadbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 422: resources_core.FormatString(data.ResourceRealm), + 424: resources_core.FormatInt64(data.ResourceCPU), + 425: resources_core.FormatInt64(data.ResourceMemory), + 426: resources_core.FormatInt64(data.ResourceDisk), + 427: resources_core.FormatInt64(data.ResourceInstances), + 428: resources_core.FormatBool(data.NeedExternalAddressMaster), + 429: resources_core.FormatString(data.IpSpaceNameMaster), + 430: resources_core.FormatString(data.ExtBACKUPSCHEDULE), + 431: resources_core.FormatString(data.AppVersion), + 432: resources_core.FormatBool(data.AutoScale), + 433: resources_core.FormatInt64(data.AutoScalePercentage), + 434: resources_core.FormatInt64(data.AutoScaleTechWindow), + 435: resources_core.FormatInt64(data.AutoScaleQuotaGb), + 459: resources_core.FormatString(data.S3Uid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 115, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "int64"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MariadbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MariadbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 115, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "int64"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MariadbModel + var state MariadbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPSCHEDULE.IsNull() != state.ExtBACKUPSCHEDULE.IsNull() || plan.ExtBACKUPSCHEDULE.IsUnknown() != state.ExtBACKUPSCHEDULE.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPSCHEDULE.IsNull() && !plan.ExtBACKUPSCHEDULE.IsUnknown() && !state.ExtBACKUPSCHEDULE.IsNull() && !state.ExtBACKUPSCHEDULE.IsUnknown() { + if plan.ExtBACKUPSCHEDULE.ValueString() != state.ExtBACKUPSCHEDULE.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScale.IsNull() != state.AutoScale.IsNull() || plan.AutoScale.IsUnknown() != state.AutoScale.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScale.IsNull() && !plan.AutoScale.IsUnknown() && !state.AutoScale.IsNull() && !state.AutoScale.IsUnknown() { + if plan.AutoScale.ValueBool() != state.AutoScale.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScalePercentage.IsNull() != state.AutoScalePercentage.IsNull() || plan.AutoScalePercentage.IsUnknown() != state.AutoScalePercentage.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScalePercentage.IsNull() && !plan.AutoScalePercentage.IsUnknown() && !state.AutoScalePercentage.IsNull() && !state.AutoScalePercentage.IsUnknown() { + if plan.AutoScalePercentage.ValueInt64() != state.AutoScalePercentage.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScaleTechWindow.IsNull() != state.AutoScaleTechWindow.IsNull() || plan.AutoScaleTechWindow.IsUnknown() != state.AutoScaleTechWindow.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScaleTechWindow.IsNull() && !plan.AutoScaleTechWindow.IsUnknown() && !state.AutoScaleTechWindow.IsNull() && !state.AutoScaleTechWindow.IsUnknown() { + if plan.AutoScaleTechWindow.ValueInt64() != state.AutoScaleTechWindow.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AutoScaleQuotaGb.IsNull() != state.AutoScaleQuotaGb.IsNull() || plan.AutoScaleQuotaGb.IsUnknown() != state.AutoScaleQuotaGb.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AutoScaleQuotaGb.IsNull() && !plan.AutoScaleQuotaGb.IsUnknown() && !state.AutoScaleQuotaGb.IsNull() && !state.AutoScaleQuotaGb.IsUnknown() { + if plan.AutoScaleQuotaGb.ValueInt64() != state.AutoScaleQuotaGb.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 436: resources_core.FormatInt64(plan.ResourceCPU), + 437: resources_core.FormatInt64(plan.ResourceMemory), + 438: resources_core.FormatInt64(plan.ResourceInstances), + 439: resources_core.FormatInt64(plan.ResourceDisk), + 440: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 441: resources_core.FormatString(plan.IpSpaceNameMaster), + 442: resources_core.FormatString(plan.ExtBACKUPSCHEDULE), + 443: resources_core.FormatBool(plan.AutoScale), + 444: resources_core.FormatInt64(plan.AutoScalePercentage), + 445: resources_core.FormatInt64(plan.AutoScaleTechWindow), + 446: resources_core.FormatInt64(plan.AutoScaleQuotaGb), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 115, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "int64"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MariadbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MariadbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *MariadbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *MariadbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/116_kafka_resource.go b/generated/prod/go/116_kafka_resource.go new file mode 100644 index 0000000..0bb9982 --- /dev/null +++ b/generated/prod/go/116_kafka_resource.go @@ -0,0 +1,469 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: kafka +// Service ID: 116 + +var _ resource.Resource = &KafkaResource{} +var _ resource.ResourceWithModifyPlan = &KafkaResource{} +var _ resource.ResourceWithImportState = &KafkaResource{} + +type KafkaResource struct { + client *core.UniversalClient +} + +type KafkaModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewKafkaResource() resource.Resource { + return &KafkaResource{} +} + +func (r *KafkaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_kafka" +} + +func (r *KafkaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`"}, + "resource_c_p_u": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2000`"}, + "resource_disk": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `20`"}, + "resource_instances": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2`"}, + "resource_memory": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2048`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *KafkaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *KafkaModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *KafkaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan KafkaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceCPU.IsNull() || config.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if config.ResourceDisk.IsNull() || config.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if config.ResourceInstances.IsNull() || config.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if config.ResourceMemory.IsNull() || config.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 461: resources_core.FormatInt64(config.ResourceInstances), + 462: resources_core.FormatInt64(config.ResourceMemory), + 463: resources_core.FormatInt64(config.ResourceCPU), + 464: resources_core.FormatInt64(config.ResourceDisk), + 465: resources_core.FormatBool(config.NeedExternalAddressMaster), + 466: resources_core.FormatString(config.IpSpaceNameMaster), + 469: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 116, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *KafkaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data KafkaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceCPU.IsNull() || data.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if data.ResourceDisk.IsNull() || data.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if data.ResourceInstances.IsNull() || data.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if data.ResourceMemory.IsNull() || data.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 461: resources_core.FormatInt64(data.ResourceInstances), + 462: resources_core.FormatInt64(data.ResourceMemory), + 463: resources_core.FormatInt64(data.ResourceCPU), + 464: resources_core.FormatInt64(data.ResourceDisk), + 465: resources_core.FormatBool(data.NeedExternalAddressMaster), + 466: resources_core.FormatString(data.IpSpaceNameMaster), + 469: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 116, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state KafkaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 116, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan KafkaModel + var state KafkaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 501: resources_core.FormatInt64(plan.ResourceInstances), + 502: resources_core.FormatInt64(plan.ResourceMemory), + 503: resources_core.FormatInt64(plan.ResourceCPU), + 504: resources_core.FormatInt64(plan.ResourceDisk), + 505: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 506: resources_core.FormatString(plan.IpSpaceNameMaster), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 116, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state KafkaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *KafkaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *KafkaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/116_kafka_topic_resource.go b/generated/prod/go/116_kafka_topic_resource.go new file mode 100644 index 0000000..1694f27 --- /dev/null +++ b/generated/prod/go/116_kafka_topic_resource.go @@ -0,0 +1,357 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: kafka +// Subresource: topic + +var _ resource.Resource = &KafkaTopicResource{} + +// KafkaTopicResource manages a subresource via operations. +type KafkaTopicResource struct { + client *core.UniversalClient +} + +type KafkaTopicModel struct { + ID types.String `tfsdk:"id"` + KafkaID types.String `tfsdk:"kafka_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + NameTopic types.String `tfsdk:"name_topic"` + Partitions types.Int64 `tfsdk:"partitions"` + Replicas types.Int64 `tfsdk:"replicas"` +} + +func NewKafkaTopicResource() resource.Resource { + return &KafkaTopicResource{} +} + +func (r *KafkaTopicResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_kafka_topic" +} + +func (r *KafkaTopicResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "kafka_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций можно увеличить, но уменьшить их нельзя."}, + "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество реплик не может привышать количество реплик брокера"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *KafkaTopicResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan KafkaTopicModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if plan.Partitions.IsNull() || plan.Partitions.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") + return + } + if plan.Replicas.IsNull() || plan.Replicas.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + } + identityCodes := []string{ + "nameTopic", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + "partitions": resources_core.FormatInt64(plan.Partitions), + "replicas": resources_core.FormatInt64(plan.Replicas), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaTopicResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state KafkaTopicModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaTopicResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan KafkaTopicModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "modify_topic" == "" { + var state KafkaTopicModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + "partitions": resources_core.FormatInt64(plan.Partitions), + "replicas": resources_core.FormatInt64(plan.Replicas), + }) + + idParams := map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_topic", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaTopicResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state KafkaTopicModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_topic" == "" { + return + } + + instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "nameTopic": resources_core.FormatString(state.NameTopic), + } + identityCodes := []string{ + "nameTopic", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(state.NameTopic), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_topic", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *KafkaTopicResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/116_kafka_user_resource.go b/generated/prod/go/116_kafka_user_resource.go new file mode 100644 index 0000000..bc12dd9 --- /dev/null +++ b/generated/prod/go/116_kafka_user_resource.go @@ -0,0 +1,373 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: kafka +// Subresource: user + +var _ resource.Resource = &KafkaUserResource{} + +// KafkaUserResource manages a subresource via operations. +type KafkaUserResource struct { + client *core.UniversalClient +} + +type KafkaUserModel struct { + ID types.String `tfsdk:"id"` + KafkaID types.String `tfsdk:"kafka_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AccessHosts types.String `tfsdk:"access_hosts"` + Group types.String `tfsdk:"group"` + NameTopic types.String `tfsdk:"name_topic"` + Operations types.String `tfsdk:"operations"` + Username types.String `tfsdk:"username"` +} + +func NewKafkaUserResource() resource.Resource { + return &KafkaUserResource{} +} + +func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_kafka_user" +} + +func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "kafka_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "access_hosts": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `192.168.1.0/24` или `*`"}, + "group": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `order-consumers` или `*`"}, + "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`"}, + "operations": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `Create, Describe, Read, Write`"}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `username-admin` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *KafkaUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan KafkaUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.AccessHosts.IsNull() || plan.AccessHosts.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "access_hosts is required.") + return + } + if plan.Group.IsNull() || plan.Group.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "group is required.") + return + } + if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if plan.Operations.IsNull() || plan.Operations.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "operations is required.") + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "nameTopic": resources_core.FormatString(plan.NameTopic), + "operations": resources_core.FormatString(plan.Operations), + "group": resources_core.FormatString(plan.Group), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state KafkaUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *KafkaUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan KafkaUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "modify_user" == "" { + var state KafkaUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{ + "nameTopic": resources_core.FormatString(plan.NameTopic), + "operations": resources_core.FormatString(plan.Operations), + "group": resources_core.FormatString(plan.Group), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "username": resources_core.FormatString(plan.Username), + }) + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *KafkaUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state KafkaUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *KafkaUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/117_nifi_resource.go b/generated/prod/go/117_nifi_resource.go new file mode 100644 index 0000000..4f1ce03 --- /dev/null +++ b/generated/prod/go/117_nifi_resource.go @@ -0,0 +1,461 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nifi +// Service ID: 117 + +var _ resource.Resource = &NifiResource{} +var _ resource.ResourceWithModifyPlan = &NifiResource{} +var _ resource.ResourceWithImportState = &NifiResource{} + +type NifiResource struct { + client *core.UniversalClient +} + +type NifiModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + KafkaUid types.String `tfsdk:"kafka_uid"` + NameTopic types.String `tfsdk:"name_topic"` + Partitions types.Int64 `tfsdk:"partitions"` + Replicas types.Int64 `tfsdk:"replicas"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNifiResource() resource.Resource { + return &NifiResource{} +} + +func (r *NifiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nifi" +} + +func (r *NifiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "kafka_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать кластер из списка Не изменяется после создания."}, + "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Не изменяется после создания."}, + "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50`"}, + "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество партиций ≤ (число брокеров). Пример `2`"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NifiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NifiModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NifiModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NifiModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, plan.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != plan.KafkaUid.ValueString() { + plan.KafkaUid = types.StringValue(resolvedKafkaUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + if plan.KafkaUid.ValueString() != state.KafkaUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить kafka_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NameTopic.IsNull() && !plan.NameTopic.IsUnknown() && !state.NameTopic.IsNull() && !state.NameTopic.IsUnknown() { + if plan.NameTopic.ValueString() != state.NameTopic.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить name_topic", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.KafkaUid.IsNull() || config.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if config.NameTopic.IsNull() || config.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if config.Partitions.IsNull() || config.Partitions.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") + return + } + if config.Replicas.IsNull() || config.Replicas.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { + config.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + params := map[int]string{ + 471: resources_core.FormatString(config.KafkaUid), + 472: resources_core.FormatInt64(config.Partitions), + 473: resources_core.FormatInt64(config.Replicas), + 474: resources_core.FormatString(config.NameTopic), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 117, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NifiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NifiModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if data.NameTopic.IsNull() || data.NameTopic.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") + return + } + if data.Partitions.IsNull() || data.Partitions.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") + return + } + if data.Replicas.IsNull() || data.Replicas.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") + return + } + if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { + data.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 117, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 471: resources_core.FormatString(data.KafkaUid), + 472: resources_core.FormatInt64(data.Partitions), + 473: resources_core.FormatInt64(data.Replicas), + 474: resources_core.FormatString(data.NameTopic), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 117, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 117, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "nameTopic", Field: "NameTopic", Type: "string"}, + {Code: "partitions", Field: "Partitions", Type: "int64"}, + {Code: "replicas", Field: "Replicas", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { + state.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NifiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NifiModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 117, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "nameTopic", Field: "NameTopic", Type: "string"}, + {Code: "partitions", Field: "Partitions", Type: "int64"}, + {Code: "replicas", Field: "Replicas", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { + newState.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NifiModel + var state NifiModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.Partitions.IsNull() != state.Partitions.IsNull() || plan.Partitions.IsUnknown() != state.Partitions.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.Partitions.IsNull() && !plan.Partitions.IsUnknown() && !state.Partitions.IsNull() && !state.Partitions.IsUnknown() { + if plan.Partitions.ValueInt64() != state.Partitions.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.Replicas.IsNull() != state.Replicas.IsNull() || plan.Replicas.IsUnknown() != state.Replicas.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.Replicas.IsNull() && !plan.Replicas.IsUnknown() && !state.Replicas.IsNull() && !state.Replicas.IsUnknown() { + if plan.Replicas.ValueInt64() != state.Replicas.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 477: resources_core.FormatInt64(plan.Partitions), + 478: resources_core.FormatInt64(plan.Replicas), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 117, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "nameTopic", Field: "NameTopic", Type: "string"}, + {Code: "partitions", Field: "Partitions", Type: "int64"}, + {Code: "replicas", Field: "Replicas", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { + state.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NifiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NifiModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NifiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NifiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/119_akhq_resource.go b/generated/prod/go/119_akhq_resource.go new file mode 100644 index 0000000..71a69ec --- /dev/null +++ b/generated/prod/go/119_akhq_resource.go @@ -0,0 +1,485 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: akhq +// Service ID: 119 + +var _ resource.Resource = &AkhqResource{} +var _ resource.ResourceWithModifyPlan = &AkhqResource{} +var _ resource.ResourceWithImportState = &AkhqResource{} + +type AkhqResource struct { + client *core.UniversalClient +} + +type AkhqModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + KafkaUid types.String `tfsdk:"kafka_uid"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewAkhqResource() resource.Resource { + return &AkhqResource{} +} + +func (r *AkhqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_akhq" +} + +func (r *AkhqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "kafka_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать кластер из списка"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `1000` Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `1024` Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *AkhqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *AkhqModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *AkhqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan AkhqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, plan.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != plan.KafkaUid.ValueString() { + plan.KafkaUid = types.StringValue(resolvedKafkaUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.KafkaUid.IsNull() || config.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { + config.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + params := map[int]string{ + 550: resources_core.FormatString(config.ResourceRealm), + 551: resources_core.FormatInt64(config.ResourceMemory), + 552: resources_core.FormatInt64(config.ResourceCPU), + 553: resources_core.FormatString(config.Domain), + 554: resources_core.FormatInt64(config.ResourceInstances), + 555: resources_core.FormatString(config.KafkaUid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 119, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *AkhqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data AkhqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Domain.IsNull() || data.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { + data.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 550: resources_core.FormatString(data.ResourceRealm), + 551: resources_core.FormatInt64(data.ResourceMemory), + 552: resources_core.FormatInt64(data.ResourceCPU), + 553: resources_core.FormatString(data.Domain), + 554: resources_core.FormatInt64(data.ResourceInstances), + 555: resources_core.FormatString(data.KafkaUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 119, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { + state.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *AkhqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state AkhqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 119, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { + newState.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan AkhqModel + var state AkhqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.KafkaUid.IsNull() != state.KafkaUid.IsNull() || plan.KafkaUid.IsUnknown() != state.KafkaUid.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + if plan.KafkaUid.ValueString() != state.KafkaUid.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 565: resources_core.FormatString(plan.KafkaUid), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 119, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { + resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) + } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { + state.KafkaUid = types.StringValue(resolvedKafkaUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *AkhqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state AkhqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *AkhqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *AkhqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/120_clickhouse_database_resource.go b/generated/prod/go/120_clickhouse_database_resource.go new file mode 100644 index 0000000..fd2d91e --- /dev/null +++ b/generated/prod/go/120_clickhouse_database_resource.go @@ -0,0 +1,339 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: clickhouse +// Subresource: database + +var _ resource.Resource = &ClickhouseDatabaseResource{} + +// ClickhouseDatabaseResource manages a subresource via operations. +type ClickhouseDatabaseResource struct { + client *core.UniversalClient +} + +type ClickhouseDatabaseModel struct { + ID types.String `tfsdk:"id"` + ClickhouseID types.String `tfsdk:"clickhouse_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + DatabaseName types.String `tfsdk:"database_name"` +} + +func NewClickhouseDatabaseResource() resource.Resource { + return &ClickhouseDatabaseResource{} +} + +func (r *ClickhouseDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_clickhouse_database" +} + +func (r *ClickhouseDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "clickhouse_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "database_name": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ClickhouseDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan ClickhouseDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.DatabaseName.IsNull() || plan.DatabaseName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "database_name is required.") + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ClickhouseDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ClickhouseDatabaseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state ClickhouseDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(plan.DatabaseName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ClickhouseDatabaseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_database" == "" { + return + } + + instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "databaseName": resources_core.FormatString(state.DatabaseName), + } + identityCodes := []string{ + "databaseName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "databaseName": resources_core.FormatString(state.DatabaseName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *ClickhouseDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/120_clickhouse_resource.go b/generated/prod/go/120_clickhouse_resource.go new file mode 100644 index 0000000..6245b18 --- /dev/null +++ b/generated/prod/go/120_clickhouse_resource.go @@ -0,0 +1,617 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: clickhouse +// Service ID: 120 + +var _ resource.Resource = &ClickhouseResource{} +var _ resource.ResourceWithModifyPlan = &ClickhouseResource{} +var _ resource.ResourceWithImportState = &ClickhouseResource{} + +type ClickhouseResource struct { + client *core.UniversalClient +} + +type ClickhouseModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AppVersion types.String `tfsdk:"app_version"` + ExtBACKUPNUMTORETAIN types.Int64 `tfsdk:"ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n"` + ExtBACKUPSCHEDULE types.String `tfsdk:"ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedCreateKeeper types.Bool `tfsdk:"need_create_keeper"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceCPUKeeper types.Int64 `tfsdk:"resource_c_p_u_keeper"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceInstancesKeeper types.Int64 `tfsdk:"resource_instances_keeper"` + ResourceInstancesShards types.Int64 `tfsdk:"resource_instances_shards"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceMemoryKeeper types.Int64 `tfsdk:"resource_memory_keeper"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewClickhouseResource() resource.Resource { + return &ClickhouseResource{} +} + +func (r *ClickhouseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_clickhouse" +} + +func (r *ClickhouseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("25.10"), MarkdownDescription: "Версия `clickhouse-server`"}, + "ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7), MarkdownDescription: "Пример: `7`"}, + "ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *"), MarkdownDescription: "Принимает формат `CRON`"}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, + "need_create_keeper": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходим для кластеризации ClickHouse"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Позволяет подключиться извне к ClickHouse"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `1000`"}, + "resource_c_p_u_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50), MarkdownDescription: "Пример: `100`"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `100`"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `3`"}, + "resource_instances_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_instances_shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1536), MarkdownDescription: "Пример: `2048`"}, + "resource_memory_keeper": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(152), MarkdownDescription: "Пример: `256`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ClickhouseResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *ClickhouseModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *ClickhouseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan ClickhouseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 572: resources_core.FormatString(config.ResourceRealm), + 573: resources_core.FormatString(config.S3Uid), + 574: resources_core.FormatInt64(config.ResourceInstances), + 575: resources_core.FormatInt64(config.ResourceInstancesShards), + 576: resources_core.FormatInt64(config.ResourceCPU), + 577: resources_core.FormatInt64(config.ResourceMemory), + 578: resources_core.FormatString(config.AppVersion), + 579: resources_core.FormatInt64(config.ResourceDisk), + 580: resources_core.FormatBool(config.NeedExternalAddressMaster), + 581: resources_core.FormatString(config.IpSpaceNameMaster), + 582: resources_core.FormatBool(config.NeedCreateKeeper), + 583: resources_core.FormatInt64(config.ResourceCPUKeeper), + 584: resources_core.FormatInt64(config.ResourceMemoryKeeper), + 585: resources_core.FormatInt64(config.ResourceInstancesKeeper), + 586: resources_core.FormatString(config.ExtBACKUPSCHEDULE), + 587: resources_core.FormatInt64(config.ExtBACKUPNUMTORETAIN), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 120, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *ClickhouseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data ClickhouseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 572: resources_core.FormatString(data.ResourceRealm), + 573: resources_core.FormatString(data.S3Uid), + 574: resources_core.FormatInt64(data.ResourceInstances), + 575: resources_core.FormatInt64(data.ResourceInstancesShards), + 576: resources_core.FormatInt64(data.ResourceCPU), + 577: resources_core.FormatInt64(data.ResourceMemory), + 578: resources_core.FormatString(data.AppVersion), + 579: resources_core.FormatInt64(data.ResourceDisk), + 580: resources_core.FormatBool(data.NeedExternalAddressMaster), + 581: resources_core.FormatString(data.IpSpaceNameMaster), + 582: resources_core.FormatBool(data.NeedCreateKeeper), + 583: resources_core.FormatInt64(data.ResourceCPUKeeper), + 584: resources_core.FormatInt64(data.ResourceMemoryKeeper), + 585: resources_core.FormatInt64(data.ResourceInstancesKeeper), + 586: resources_core.FormatString(data.ExtBACKUPSCHEDULE), + 587: resources_core.FormatInt64(data.ExtBACKUPNUMTORETAIN), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 120, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ClickhouseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 120, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ClickhouseModel + var state ClickhouseModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstancesShards.IsNull() != state.ResourceInstancesShards.IsNull() || plan.ResourceInstancesShards.IsUnknown() != state.ResourceInstancesShards.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstancesShards.IsNull() && !plan.ResourceInstancesShards.IsUnknown() && !state.ResourceInstancesShards.IsNull() && !state.ResourceInstancesShards.IsUnknown() { + if plan.ResourceInstancesShards.ValueInt64() != state.ResourceInstancesShards.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedCreateKeeper.IsNull() != state.NeedCreateKeeper.IsNull() || plan.NeedCreateKeeper.IsUnknown() != state.NeedCreateKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedCreateKeeper.IsNull() && !plan.NeedCreateKeeper.IsUnknown() && !state.NeedCreateKeeper.IsNull() && !state.NeedCreateKeeper.IsUnknown() { + if plan.NeedCreateKeeper.ValueBool() != state.NeedCreateKeeper.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPUKeeper.IsNull() != state.ResourceCPUKeeper.IsNull() || plan.ResourceCPUKeeper.IsUnknown() != state.ResourceCPUKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPUKeeper.IsNull() && !plan.ResourceCPUKeeper.IsUnknown() && !state.ResourceCPUKeeper.IsNull() && !state.ResourceCPUKeeper.IsUnknown() { + if plan.ResourceCPUKeeper.ValueInt64() != state.ResourceCPUKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemoryKeeper.IsNull() != state.ResourceMemoryKeeper.IsNull() || plan.ResourceMemoryKeeper.IsUnknown() != state.ResourceMemoryKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemoryKeeper.IsNull() && !plan.ResourceMemoryKeeper.IsUnknown() && !state.ResourceMemoryKeeper.IsNull() && !state.ResourceMemoryKeeper.IsUnknown() { + if plan.ResourceMemoryKeeper.ValueInt64() != state.ResourceMemoryKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstancesKeeper.IsNull() != state.ResourceInstancesKeeper.IsNull() || plan.ResourceInstancesKeeper.IsUnknown() != state.ResourceInstancesKeeper.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstancesKeeper.IsNull() && !plan.ResourceInstancesKeeper.IsUnknown() && !state.ResourceInstancesKeeper.IsNull() && !state.ResourceInstancesKeeper.IsUnknown() { + if plan.ResourceInstancesKeeper.ValueInt64() != state.ResourceInstancesKeeper.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPSCHEDULE.IsNull() != state.ExtBACKUPSCHEDULE.IsNull() || plan.ExtBACKUPSCHEDULE.IsUnknown() != state.ExtBACKUPSCHEDULE.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPSCHEDULE.IsNull() && !plan.ExtBACKUPSCHEDULE.IsUnknown() && !state.ExtBACKUPSCHEDULE.IsNull() && !state.ExtBACKUPSCHEDULE.IsUnknown() { + if plan.ExtBACKUPSCHEDULE.ValueString() != state.ExtBACKUPSCHEDULE.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPNUMTORETAIN.IsNull() != state.ExtBACKUPNUMTORETAIN.IsNull() || plan.ExtBACKUPNUMTORETAIN.IsUnknown() != state.ExtBACKUPNUMTORETAIN.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPNUMTORETAIN.IsNull() && !plan.ExtBACKUPNUMTORETAIN.IsUnknown() && !state.ExtBACKUPNUMTORETAIN.IsNull() && !state.ExtBACKUPNUMTORETAIN.IsUnknown() { + if plan.ExtBACKUPNUMTORETAIN.ValueInt64() != state.ExtBACKUPNUMTORETAIN.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 603: resources_core.FormatInt64(plan.ResourceInstances), + 604: resources_core.FormatInt64(plan.ResourceInstancesShards), + 605: resources_core.FormatInt64(plan.ResourceCPU), + 606: resources_core.FormatInt64(plan.ResourceMemory), + 607: resources_core.FormatString(plan.AppVersion), + 608: resources_core.FormatInt64(plan.ResourceDisk), + 609: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 610: resources_core.FormatString(plan.IpSpaceNameMaster), + 611: resources_core.FormatBool(plan.NeedCreateKeeper), + 612: resources_core.FormatInt64(plan.ResourceCPUKeeper), + 613: resources_core.FormatInt64(plan.ResourceMemoryKeeper), + 614: resources_core.FormatInt64(plan.ResourceInstancesKeeper), + 615: resources_core.FormatString(plan.ExtBACKUPSCHEDULE), + 616: resources_core.FormatInt64(plan.ExtBACKUPNUMTORETAIN), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 120, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needCreateKeeper", Field: "NeedCreateKeeper", Type: "bool"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceCPUKeeper", Field: "ResourceCPUKeeper", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceInstancesKeeper", Field: "ResourceInstancesKeeper", Type: "int64"}, + {Code: "resourceInstancesShards", Field: "ResourceInstancesShards", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceMemoryKeeper", Field: "ResourceMemoryKeeper", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ClickhouseModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *ClickhouseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *ClickhouseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/120_clickhouse_user_resource.go b/generated/prod/go/120_clickhouse_user_resource.go new file mode 100644 index 0000000..4eb5176 --- /dev/null +++ b/generated/prod/go/120_clickhouse_user_resource.go @@ -0,0 +1,356 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: clickhouse +// Subresource: user + +var _ resource.Resource = &ClickhouseUserResource{} + +// ClickhouseUserResource manages a subresource via operations. +type ClickhouseUserResource struct { + client *core.UniversalClient +} + +type ClickhouseUserModel struct { + ID types.String `tfsdk:"id"` + ClickhouseID types.String `tfsdk:"clickhouse_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AccessHosts types.String `tfsdk:"access_hosts"` + DatabaseName types.String `tfsdk:"database_name"` + Username types.String `tfsdk:"username"` +} + +func NewClickhouseUserResource() resource.Resource { + return &ClickhouseUserResource{} +} + +func (r *ClickhouseUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_clickhouse_user" +} + +func (r *ClickhouseUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "clickhouse_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("::/0"), MarkdownDescription: "Пример: `::/0` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "database_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"database\"]"), MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ClickhouseUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan ClickhouseUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "accessHosts": resources_core.FormatString(plan.AccessHosts), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ClickhouseUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ClickhouseUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ClickhouseUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state ClickhouseUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(plan.AccessHosts), + "databaseName": resources_core.FormatString(plan.DatabaseName), + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *ClickhouseUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ClickhouseUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "accessHosts": resources_core.FormatString(state.AccessHosts), + "databaseName": resources_core.FormatString(state.DatabaseName), + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "accessHosts", + "databaseName", + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *ClickhouseUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/12_s3_resource.go b/generated/prod/go/12_s3_resource.go new file mode 100644 index 0000000..5387016 --- /dev/null +++ b/generated/prod/go/12_s3_resource.go @@ -0,0 +1,401 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: s3 +// Service ID: 12 + +var _ resource.Resource = &S3Resource{} +var _ resource.ResourceWithModifyPlan = &S3Resource{} +var _ resource.ResourceWithImportState = &S3Resource{} + +type S3Resource struct { + client *core.UniversalClient +} + +type S3Model struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + DisplayName types.String `tfsdk:"display_name"` + MaxBucketsPerUser types.Int64 `tfsdk:"max_buckets_per_user"` + MaxObjectsPerBucket types.Int64 `tfsdk:"max_objects_per_bucket"` + MaxSizeGbPerUser types.Int64 `tfsdk:"max_size_gb_per_user"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewS3Resource() resource.Resource { + return &S3Resource{} +} + +func (r *S3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_s3" +} + +func (r *S3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "display_name": schema.StringAttribute{Required: true, MarkdownDescription: "Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента Не изменяется после создания."}, + "max_buckets_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на пользователя. Количество бакетов пользователя. Передаём руками"}, + "max_objects_per_bucket": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на бакет. Кол-во объектов. Передаём руками"}, + "max_size_gb_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "maxSizeGb. Передаём руками"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *S3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *S3Model + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *S3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan S3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.DisplayName.IsNull() && !plan.DisplayName.IsUnknown() && !state.DisplayName.IsNull() && !state.DisplayName.IsUnknown() { + if plan.DisplayName.ValueString() != state.DisplayName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить display_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.DisplayName.IsNull() || config.DisplayName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 163: resources_core.FormatString(config.ResourceRealm), + 320: resources_core.FormatString(config.DisplayName), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 12, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *S3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data S3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.DisplayName.IsNull() || data.DisplayName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 163: resources_core.FormatString(data.ResourceRealm), + 320: resources_core.FormatString(data.DisplayName), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 12, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "displayName", Field: "DisplayName", Type: "string"}, + {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, + {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, + {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state S3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 12, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "displayName", Field: "DisplayName", Type: "string"}, + {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, + {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, + {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan S3Model + var state S3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.MaxSizeGbPerUser.IsNull() != state.MaxSizeGbPerUser.IsNull() || plan.MaxSizeGbPerUser.IsUnknown() != state.MaxSizeGbPerUser.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MaxSizeGbPerUser.IsNull() && !plan.MaxSizeGbPerUser.IsUnknown() && !state.MaxSizeGbPerUser.IsNull() && !state.MaxSizeGbPerUser.IsUnknown() { + if plan.MaxSizeGbPerUser.ValueInt64() != state.MaxSizeGbPerUser.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MaxObjectsPerBucket.IsNull() != state.MaxObjectsPerBucket.IsNull() || plan.MaxObjectsPerBucket.IsUnknown() != state.MaxObjectsPerBucket.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MaxObjectsPerBucket.IsNull() && !plan.MaxObjectsPerBucket.IsUnknown() && !state.MaxObjectsPerBucket.IsNull() && !state.MaxObjectsPerBucket.IsUnknown() { + if plan.MaxObjectsPerBucket.ValueInt64() != state.MaxObjectsPerBucket.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MaxBucketsPerUser.IsNull() != state.MaxBucketsPerUser.IsNull() || plan.MaxBucketsPerUser.IsUnknown() != state.MaxBucketsPerUser.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MaxBucketsPerUser.IsNull() && !plan.MaxBucketsPerUser.IsUnknown() && !state.MaxBucketsPerUser.IsNull() && !state.MaxBucketsPerUser.IsUnknown() { + if plan.MaxBucketsPerUser.ValueInt64() != state.MaxBucketsPerUser.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), + 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), + 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 12, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "displayName", Field: "DisplayName", Type: "string"}, + {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, + {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, + {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state S3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *S3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *S3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/12_s3_sub_user_resource.go b/generated/prod/go/12_s3_sub_user_resource.go new file mode 100644 index 0000000..66cc0ab --- /dev/null +++ b/generated/prod/go/12_s3_sub_user_resource.go @@ -0,0 +1,351 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: s3 +// Subresource: sub_user + +var _ resource.Resource = &S3SubUserResource{} + +// S3SubUserResource manages a subresource via operations. +type S3SubUserResource struct { + client *core.UniversalClient +} + +type S3SubUserModel struct { + ID types.String `tfsdk:"id"` + S3ID types.String `tfsdk:"s3_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + SubUserAccess types.String `tfsdk:"sub_user_access"` + SubUserName types.String `tfsdk:"sub_user_name"` +} + +func NewS3SubUserResource() resource.Resource { + return &S3SubUserResource{} +} + +func (r *S3SubUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_s3_sub_user" +} + +func (r *S3SubUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "s3_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "sub_user_access": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + "sub_user_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *S3SubUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan S3SubUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.SubUserAccess.IsNull() || plan.SubUserAccess.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "sub_user_access is required.") + return + } + if plan.SubUserName.IsNull() || plan.SubUserName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "sub_user_name is required.") + return + } + + instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "subUserAccess": resources_core.FormatString(plan.SubUserAccess), + "subUserName": resources_core.FormatString(plan.SubUserName), + } + identityCodes := []string{ + "subUserAccess", + "subUserName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "subUserName": resources_core.FormatString(plan.SubUserName), + "subUserAccess": resources_core.FormatString(plan.SubUserAccess), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *S3SubUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state S3SubUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3SubUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan S3SubUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "" == "" { + var state S3SubUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{}) + + idParams := map[string]string{ + "subUserAccess": resources_core.FormatString(plan.SubUserAccess), + "subUserName": resources_core.FormatString(plan.SubUserName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *S3SubUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state S3SubUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_sub_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.S3ID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "subUserAccess": resources_core.FormatString(state.SubUserAccess), + "subUserName": resources_core.FormatString(state.SubUserName), + } + identityCodes := []string{ + "subUserAccess", + "subUserName", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "subUserName": resources_core.FormatString(state.SubUserName), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_sub_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *S3SubUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/13_s3bucket_resource.go b/generated/prod/go/13_s3bucket_resource.go new file mode 100644 index 0000000..37210ea --- /dev/null +++ b/generated/prod/go/13_s3bucket_resource.go @@ -0,0 +1,439 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: s3bucket +// Service ID: 13 + +var _ resource.Resource = &S3bucketResource{} +var _ resource.ResourceWithModifyPlan = &S3bucketResource{} +var _ resource.ResourceWithImportState = &S3bucketResource{} + +type S3bucketResource struct { + client *core.UniversalClient +} + +type S3bucketModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + BucketName types.String `tfsdk:"bucket_name"` + CorsAll types.Bool `tfsdk:"cors_all"` + ListAll types.Bool `tfsdk:"list_all"` + MaxSize types.String `tfsdk:"max_size"` + Placement types.String `tfsdk:"placement"` + ReadAll types.Bool `tfsdk:"read_all"` + S3UserUid types.String `tfsdk:"s3_user_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewS3bucketResource() resource.Resource { + return &S3bucketResource{} +} + +func (r *S3bucketResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_s3bucket" +} + +func (r *S3bucketResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "bucket_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя бакета s3 Не изменяется после создания."}, + "cors_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS Не изменяется после создания."}, + "list_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. Не изменяется после создания."}, + "max_size": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("-1"), MarkdownDescription: "Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes Не изменяется после создания."}, + "placement": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("HOT"), MarkdownDescription: "Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения Не изменяется после создания."}, + "read_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. Не изменяется после создания."}, + "s3_user_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UID Корневой услуги S3. На основе него будет создан бакет. Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *S3bucketResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *S3bucketModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *S3bucketModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan S3bucketModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() { + resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3UserUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) + } else if resolvedS3UserUid != "" && resolvedS3UserUid != plan.S3UserUid.ValueString() { + plan.S3UserUid = types.StringValue(resolvedS3UserUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.BucketName.IsNull() && !plan.BucketName.IsUnknown() && !state.BucketName.IsNull() && !state.BucketName.IsUnknown() { + if plan.BucketName.ValueString() != state.BucketName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить bucket_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.CorsAll.IsNull() && !plan.CorsAll.IsUnknown() && !state.CorsAll.IsNull() && !state.CorsAll.IsUnknown() { + if plan.CorsAll.ValueBool() != state.CorsAll.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить cors_all", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ListAll.IsNull() && !plan.ListAll.IsUnknown() && !state.ListAll.IsNull() && !state.ListAll.IsUnknown() { + if plan.ListAll.ValueBool() != state.ListAll.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить list_all", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.MaxSize.IsNull() && !plan.MaxSize.IsUnknown() && !state.MaxSize.IsNull() && !state.MaxSize.IsUnknown() { + if plan.MaxSize.ValueString() != state.MaxSize.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить max_size", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Placement.IsNull() && !plan.Placement.IsUnknown() && !state.Placement.IsNull() && !state.Placement.IsUnknown() { + if plan.Placement.ValueString() != state.Placement.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить placement", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ReadAll.IsNull() && !plan.ReadAll.IsUnknown() && !state.ReadAll.IsNull() && !state.ReadAll.IsUnknown() { + if plan.ReadAll.ValueBool() != state.ReadAll.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить read_all", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() && !state.S3UserUid.IsNull() && !state.S3UserUid.IsUnknown() { + if plan.S3UserUid.ValueString() != state.S3UserUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_user_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.BucketName.IsNull() || config.BucketName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") + return + } + if config.S3UserUid.IsNull() || config.S3UserUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3UserUid.IsNull() && !config.S3UserUid.IsUnknown() { + resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3UserUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) + } else if resolvedS3UserUid != "" && resolvedS3UserUid != config.S3UserUid.ValueString() { + config.S3UserUid = types.StringValue(resolvedS3UserUid) + } + } + params := map[int]string{ + 124: resources_core.FormatString(config.S3UserUid), + 125: resources_core.FormatString(config.BucketName), + 126: resources_core.FormatString(config.MaxSize), + 127: resources_core.FormatBool(config.ReadAll), + 128: resources_core.FormatBool(config.ListAll), + 129: resources_core.FormatBool(config.CorsAll), + 130: resources_core.FormatString(config.Placement), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 13, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *S3bucketResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data S3bucketModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.BucketName.IsNull() || data.BucketName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") + return + } + if data.S3UserUid.IsNull() || data.S3UserUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 124: resources_core.FormatString(data.S3UserUid), + 125: resources_core.FormatString(data.BucketName), + 126: resources_core.FormatString(data.MaxSize), + 127: resources_core.FormatBool(data.ReadAll), + 128: resources_core.FormatBool(data.ListAll), + 129: resources_core.FormatBool(data.CorsAll), + 130: resources_core.FormatString(data.Placement), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 13, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "bucketName", Field: "BucketName", Type: "string"}, + {Code: "corsAll", Field: "CorsAll", Type: "bool"}, + {Code: "listAll", Field: "ListAll", Type: "bool"}, + {Code: "maxSize", Field: "MaxSize", Type: "string"}, + {Code: "placement", Field: "Placement", Type: "string"}, + {Code: "readAll", Field: "ReadAll", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3bucketResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state S3bucketModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 13, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "bucketName", Field: "BucketName", Type: "string"}, + {Code: "corsAll", Field: "CorsAll", Type: "bool"}, + {Code: "listAll", Field: "ListAll", Type: "bool"}, + {Code: "maxSize", Field: "MaxSize", Type: "string"}, + {Code: "placement", Field: "Placement", Type: "string"}, + {Code: "readAll", Field: "ReadAll", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan S3bucketModel + var state S3bucketModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 13, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "bucketName", Field: "BucketName", Type: "string"}, + {Code: "corsAll", Field: "CorsAll", Type: "bool"}, + {Code: "listAll", Field: "ListAll", Type: "bool"}, + {Code: "maxSize", Field: "MaxSize", Type: "string"}, + {Code: "placement", Field: "Placement", Type: "string"}, + {Code: "readAll", Field: "ReadAll", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *S3bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state S3bucketModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *S3bucketResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *S3bucketResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/149_valo_tenant_resource.go b/generated/prod/go/149_valo_tenant_resource.go new file mode 100644 index 0000000..9aef80e --- /dev/null +++ b/generated/prod/go/149_valo_tenant_resource.go @@ -0,0 +1,398 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: valo_tenant +// Service ID: 149 + +var _ resource.Resource = &ValoTenantResource{} +var _ resource.ResourceWithModifyPlan = &ValoTenantResource{} +var _ resource.ResourceWithImportState = &ValoTenantResource{} + +type ValoTenantResource struct { + client *core.UniversalClient +} + +type ValoTenantModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Storage types.Int64 `tfsdk:"s3_storage"` + ValoOptions types.String `tfsdk:"valo_options"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewValoTenantResource() resource.Resource { + return &ValoTenantResource{} +} + +func (r *ValoTenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_valo_tenant" +} + +func (r *ValoTenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Будет добавлено в зону valo.ngcloud.ru Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "s3_storage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Параметр указывается в `Gb`"}, + "valo_options": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *ValoTenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *ValoTenantModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *ValoTenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan ValoTenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 655: resources_core.FormatString(config.ResourceRealm), + 656: resources_core.FormatString(config.Domain), + 657: resources_core.FormatString(config.ValoOptions), + 658: resources_core.FormatInt64(config.S3Storage), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 149, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *ValoTenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data ValoTenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Domain.IsNull() || data.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 655: resources_core.FormatString(data.ResourceRealm), + 656: resources_core.FormatString(data.Domain), + 657: resources_core.FormatString(data.ValoOptions), + 658: resources_core.FormatInt64(data.S3Storage), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 149, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, + {Code: "valoOptions", Field: "ValoOptions", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ValoTenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ValoTenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 149, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, + {Code: "valoOptions", Field: "ValoOptions", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ValoTenantModel + var state ValoTenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ValoOptions.IsNull() != state.ValoOptions.IsNull() || plan.ValoOptions.IsUnknown() != state.ValoOptions.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ValoOptions.IsNull() && !plan.ValoOptions.IsUnknown() && !state.ValoOptions.IsNull() && !state.ValoOptions.IsUnknown() { + if plan.ValoOptions.ValueString() != state.ValoOptions.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.S3Storage.IsNull() != state.S3Storage.IsNull() || plan.S3Storage.IsUnknown() != state.S3Storage.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.S3Storage.IsNull() && !plan.S3Storage.IsUnknown() && !state.S3Storage.IsNull() && !state.S3Storage.IsUnknown() { + if plan.S3Storage.ValueInt64() != state.S3Storage.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 659: resources_core.FormatString(plan.ValoOptions), + 660: resources_core.FormatInt64(plan.S3Storage), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 149, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, + {Code: "valoOptions", Field: "ValoOptions", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ValoTenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ValoTenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *ValoTenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *ValoTenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/12_s3_reconcile_action.go b/generated/prod/go/150_k8s_sthutrval_cluster_reconcile_action.go similarity index 65% rename from provider/internal/resources_gen/12_s3_reconcile_action.go rename to generated/prod/go/150_k8s_sthutrval_cluster_reconcile_action.go index 5fe33e3..40aa238 100644 --- a/provider/internal/resources_gen/12_s3_reconcile_action.go +++ b/generated/prod/go/150_k8s_sthutrval_cluster_reconcile_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 +// Service: k8s_sthutrval_cluster // Action: reconcile -var _ resource.Resource = &S3ReconcileResource{} +var _ resource.Resource = &K8sSthutrvalClusterReconcileResource{} -// S3ReconcileResource triggers a one-time action. -type S3ReconcileResource struct { +// K8sSthutrvalClusterReconcileResource triggers a one-time action. +type K8sSthutrvalClusterReconcileResource struct { client *core.UniversalClient } -type S3ReconcileModel struct { +type K8sSthutrvalClusterReconcileModel struct { ID types.String `tfsdk:"id"` - S3ID types.String `tfsdk:"s3_id"` + K8sSthutrvalClusterID types.String `tfsdk:"k8s_sthutrval_cluster_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewS3ReconcileResource() resource.Resource { - return &S3ReconcileResource{} +func NewK8sSthutrvalClusterReconcileResource() resource.Resource { + return &K8sSthutrvalClusterReconcileResource{} } -func (r *S3ReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3_reconcile" +func (r *K8sSthutrvalClusterReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster_reconcile" } -func (r *S3ReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *K8sSthutrvalClusterReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "s3_id": schema.StringAttribute{Required: true}, + "k8s_sthutrval_cluster_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *S3ReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan S3ReconcileModel +func (r *K8sSthutrvalClusterReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan K8sSthutrvalClusterReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,9 +71,6 @@ func (r *S3ReconcileResource) Create(ctx context.Context, req resource.CreateReq if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return @@ -85,8 +80,8 @@ func (r *S3ReconcileResource) Create(ctx context.Context, req resource.CreateReq resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *S3ReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3ReconcileModel +func (r *K8sSthutrvalClusterReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sSthutrvalClusterReconcileModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *S3ReconcileResource) Read(ctx context.Context, req resource.ReadRequest resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *S3ReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3ReconcileModel +func (r *K8sSthutrvalClusterReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sSthutrvalClusterReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) + instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -119,11 +114,11 @@ func (r *S3ReconcileResource) Update(ctx context.Context, req resource.UpdateReq resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *S3ReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *K8sSthutrvalClusterReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *S3ReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *K8sSthutrvalClusterReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/150_k8s_sthutrval_cluster_resource.go b/generated/prod/go/150_k8s_sthutrval_cluster_resource.go new file mode 100644 index 0000000..53853b4 --- /dev/null +++ b/generated/prod/go/150_k8s_sthutrval_cluster_resource.go @@ -0,0 +1,667 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_sthutrval_cluster +// Service ID: 150 + +var _ resource.Resource = &K8sSthutrvalClusterResource{} +var _ resource.ResourceWithModifyPlan = &K8sSthutrvalClusterResource{} +var _ resource.ResourceWithImportState = &K8sSthutrvalClusterResource{} + +type K8sSthutrvalClusterResource struct { + client *core.UniversalClient +} + +type K8sSthutrvalClusterModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i"` + AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress"` + AppVersion types.String `tfsdk:"app_version"` + ClusterName types.String `tfsdk:"cluster_name"` + ControlPlaneCount types.Int64 `tfsdk:"control_plane_count"` + ControlPlaneSizingDisk types.Int64 `tfsdk:"control_plane_sizing_disk"` + ControlPlaneSizingPolicy types.String `tfsdk:"control_plane_sizing_policy"` + NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i"` + NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress"` + NsxtUid types.String `tfsdk:"nsxt_uid"` + VdcUid types.String `tfsdk:"vdc_uid"` + WorkersCount types.Int64 `tfsdk:"workers_count"` + WorkersSizingDisk types.Int64 `tfsdk:"workers_sizing_disk"` + WorkersSizingPolicy types.String `tfsdk:"workers_sizing_policy"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewK8sSthutrvalClusterResource() resource.Resource { + return &K8sSthutrvalClusterResource{} +} + +func (r *K8sSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster" +} + +func (r *K8sSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, + "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.12.1"), MarkdownDescription: "Версия Штурвал (не является версией Kubernetes)"}, + "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example1"), MarkdownDescription: "Наименование кластера Kubernetes. Имя будет использоваться в fqdn (svc.$CLUSTER_NAME) Не изменяется после создания."}, + "control_plane_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, + "control_plane_sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Настройка Control Plane. Disk (Gb)"}, + "control_plane_sizing_policy": schema.StringAttribute{Required: true, MarkdownDescription: "Настройка Control Plane. CPU (шт) / MEM (gb).
Наверное, должна подтягиваться из свойств ресурсной платформы Не изменяется после создания."}, + "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false Не изменяется после создания."}, + "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress Не изменяется после создания."}, + "nsxt_uid": schema.StringAttribute{Required: true, MarkdownDescription: "InstanceUid nsxt. Nsxt должен быть создан Не изменяется после создания."}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core) Не изменяется после создания."}, + "workers_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2), MarkdownDescription: "Настройка workers. Кол-во нод"}, + "workers_sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(40), MarkdownDescription: "Настройка workers. Disk (Gb)"}, + "workers_sizing_policy": schema.StringAttribute{Required: true, MarkdownDescription: "Настройка workers. CPU (шт) / MEM (gb) Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, plan.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != plan.NsxtUid.ValueString() { + plan.NsxtUid = types.StringValue(resolvedNsxtUid) + planChanged = true + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ClusterName.IsNull() && !plan.ClusterName.IsUnknown() && !state.ClusterName.IsNull() && !state.ClusterName.IsUnknown() { + if plan.ClusterName.ValueString() != state.ClusterName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить cluster_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ControlPlaneSizingPolicy.IsNull() && !plan.ControlPlaneSizingPolicy.IsUnknown() && !state.ControlPlaneSizingPolicy.IsNull() && !state.ControlPlaneSizingPolicy.IsUnknown() { + if plan.ControlPlaneSizingPolicy.ValueString() != state.ControlPlaneSizingPolicy.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить control_plane_sizing_policy", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressAPI.IsNull() && !plan.NeedExternalAddressAPI.IsUnknown() && !state.NeedExternalAddressAPI.IsNull() && !state.NeedExternalAddressAPI.IsUnknown() { + if plan.NeedExternalAddressAPI.ValueBool() != state.NeedExternalAddressAPI.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_a_p_i", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressIngress.IsNull() && !plan.NeedExternalAddressIngress.IsUnknown() && !state.NeedExternalAddressIngress.IsNull() && !state.NeedExternalAddressIngress.IsUnknown() { + if plan.NeedExternalAddressIngress.ValueBool() != state.NeedExternalAddressIngress.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_ingress", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + if plan.NsxtUid.ValueString() != state.NsxtUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.WorkersSizingPolicy.IsNull() && !plan.WorkersSizingPolicy.IsUnknown() && !state.WorkersSizingPolicy.IsNull() && !state.WorkersSizingPolicy.IsUnknown() { + if plan.WorkersSizingPolicy.ValueString() != state.WorkersSizingPolicy.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить workers_sizing_policy", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ControlPlaneSizingPolicy.IsNull() || config.ControlPlaneSizingPolicy.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "control_plane_sizing_policy is required.") + return + } + if config.NsxtUid.IsNull() || config.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if config.VdcUid.IsNull() || config.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if config.WorkersSizingPolicy.IsNull() || config.WorkersSizingPolicy.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "workers_sizing_policy is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { + config.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + params := map[int]string{ + 344: resources_core.FormatString(config.NsxtUid), + 345: resources_core.FormatString(config.ClusterName), + 346: resources_core.FormatString(config.ControlPlaneSizingPolicy), + 347: resources_core.FormatInt64(config.ControlPlaneSizingDisk), + 348: resources_core.FormatInt64(config.ControlPlaneCount), + 349: resources_core.FormatString(config.WorkersSizingPolicy), + 350: resources_core.FormatInt64(config.WorkersSizingDisk), + 351: resources_core.FormatInt64(config.WorkersCount), + 352: resources_core.FormatString(config.AppVersion), + 353: resources_core.FormatBool(config.NeedExternalAddressAPI), + 354: resources_core.FormatString(config.AccessIpListAPI), + 355: resources_core.FormatBool(config.NeedExternalAddressIngress), + 356: resources_core.FormatString(config.AccessIpListIngress), + 747: resources_core.FormatString(config.VdcUid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 150, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *K8sSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ControlPlaneSizingPolicy.IsNull() || data.ControlPlaneSizingPolicy.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "control_plane_sizing_policy is required.") + return + } + if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if data.WorkersSizingPolicy.IsNull() || data.WorkersSizingPolicy.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "workers_sizing_policy is required.") + return + } + if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { + data.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 344: resources_core.FormatString(data.NsxtUid), + 345: resources_core.FormatString(data.ClusterName), + 346: resources_core.FormatString(data.ControlPlaneSizingPolicy), + 347: resources_core.FormatInt64(data.ControlPlaneSizingDisk), + 348: resources_core.FormatInt64(data.ControlPlaneCount), + 349: resources_core.FormatString(data.WorkersSizingPolicy), + 350: resources_core.FormatInt64(data.WorkersSizingDisk), + 351: resources_core.FormatInt64(data.WorkersCount), + 352: resources_core.FormatString(data.AppVersion), + 353: resources_core.FormatBool(data.NeedExternalAddressAPI), + 354: resources_core.FormatString(data.AccessIpListAPI), + 355: resources_core.FormatBool(data.NeedExternalAddressIngress), + 356: resources_core.FormatString(data.AccessIpListIngress), + 747: resources_core.FormatString(data.VdcUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 150, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpListAPI", Field: "AccessIpListAPI", Type: "string"}, + {Code: "accessIpListIngress", Field: "AccessIpListIngress", Type: "string"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "clusterName", Field: "ClusterName", Type: "string"}, + {Code: "controlPlaneCount", Field: "ControlPlaneCount", Type: "int64"}, + {Code: "controlPlaneSizingDisk", Field: "ControlPlaneSizingDisk", Type: "int64"}, + {Code: "controlPlaneSizingPolicy", Field: "ControlPlaneSizingPolicy", Type: "string"}, + {Code: "needExternalAddressAPI", Field: "NeedExternalAddressAPI", Type: "bool"}, + {Code: "needExternalAddressIngress", Field: "NeedExternalAddressIngress", Type: "bool"}, + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "workersCount", Field: "WorkersCount", Type: "int64"}, + {Code: "workersSizingDisk", Field: "WorkersSizingDisk", Type: "int64"}, + {Code: "workersSizingPolicy", Field: "WorkersSizingPolicy", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 150, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpListAPI", Field: "AccessIpListAPI", Type: "string"}, + {Code: "accessIpListIngress", Field: "AccessIpListIngress", Type: "string"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "clusterName", Field: "ClusterName", Type: "string"}, + {Code: "controlPlaneCount", Field: "ControlPlaneCount", Type: "int64"}, + {Code: "controlPlaneSizingDisk", Field: "ControlPlaneSizingDisk", Type: "int64"}, + {Code: "controlPlaneSizingPolicy", Field: "ControlPlaneSizingPolicy", Type: "string"}, + {Code: "needExternalAddressAPI", Field: "NeedExternalAddressAPI", Type: "bool"}, + {Code: "needExternalAddressIngress", Field: "NeedExternalAddressIngress", Type: "bool"}, + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "workersCount", Field: "WorkersCount", Type: "int64"}, + {Code: "workersSizingDisk", Field: "WorkersSizingDisk", Type: "int64"}, + {Code: "workersSizingPolicy", Field: "WorkersSizingPolicy", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { + newState.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sSthutrvalClusterModel + var state K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ControlPlaneCount.IsNull() != state.ControlPlaneCount.IsNull() || plan.ControlPlaneCount.IsUnknown() != state.ControlPlaneCount.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ControlPlaneCount.IsNull() && !plan.ControlPlaneCount.IsUnknown() && !state.ControlPlaneCount.IsNull() && !state.ControlPlaneCount.IsUnknown() { + if plan.ControlPlaneCount.ValueInt64() != state.ControlPlaneCount.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.WorkersCount.IsNull() != state.WorkersCount.IsNull() || plan.WorkersCount.IsUnknown() != state.WorkersCount.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.WorkersCount.IsNull() && !plan.WorkersCount.IsUnknown() && !state.WorkersCount.IsNull() && !state.WorkersCount.IsUnknown() { + if plan.WorkersCount.ValueInt64() != state.WorkersCount.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessIpListAPI.IsNull() != state.AccessIpListAPI.IsNull() || plan.AccessIpListAPI.IsUnknown() != state.AccessIpListAPI.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessIpListAPI.IsNull() && !plan.AccessIpListAPI.IsUnknown() && !state.AccessIpListAPI.IsNull() && !state.AccessIpListAPI.IsUnknown() { + if plan.AccessIpListAPI.ValueString() != state.AccessIpListAPI.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessIpListIngress.IsNull() != state.AccessIpListIngress.IsNull() || plan.AccessIpListIngress.IsUnknown() != state.AccessIpListIngress.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessIpListIngress.IsNull() && !plan.AccessIpListIngress.IsUnknown() && !state.AccessIpListIngress.IsNull() && !state.AccessIpListIngress.IsUnknown() { + if plan.AccessIpListIngress.ValueString() != state.AccessIpListIngress.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ControlPlaneSizingDisk.IsNull() != state.ControlPlaneSizingDisk.IsNull() || plan.ControlPlaneSizingDisk.IsUnknown() != state.ControlPlaneSizingDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ControlPlaneSizingDisk.IsNull() && !plan.ControlPlaneSizingDisk.IsUnknown() && !state.ControlPlaneSizingDisk.IsNull() && !state.ControlPlaneSizingDisk.IsUnknown() { + if plan.ControlPlaneSizingDisk.ValueInt64() != state.ControlPlaneSizingDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.WorkersSizingDisk.IsNull() != state.WorkersSizingDisk.IsNull() || plan.WorkersSizingDisk.IsUnknown() != state.WorkersSizingDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.WorkersSizingDisk.IsNull() && !plan.WorkersSizingDisk.IsUnknown() && !state.WorkersSizingDisk.IsNull() && !state.WorkersSizingDisk.IsUnknown() { + if plan.WorkersSizingDisk.ValueInt64() != state.WorkersSizingDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 362: resources_core.FormatInt64(plan.ControlPlaneCount), + 363: resources_core.FormatInt64(plan.WorkersCount), + 364: resources_core.FormatString(plan.AccessIpListAPI), + 365: resources_core.FormatString(plan.AccessIpListIngress), + 563: resources_core.FormatString(plan.AppVersion), + 637: resources_core.FormatInt64(plan.ControlPlaneSizingDisk), + 638: resources_core.FormatInt64(plan.WorkersSizingDisk), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 150, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpListAPI", Field: "AccessIpListAPI", Type: "string"}, + {Code: "accessIpListIngress", Field: "AccessIpListIngress", Type: "string"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "clusterName", Field: "ClusterName", Type: "string"}, + {Code: "controlPlaneCount", Field: "ControlPlaneCount", Type: "int64"}, + {Code: "controlPlaneSizingDisk", Field: "ControlPlaneSizingDisk", Type: "int64"}, + {Code: "controlPlaneSizingPolicy", Field: "ControlPlaneSizingPolicy", Type: "string"}, + {Code: "needExternalAddressAPI", Field: "NeedExternalAddressAPI", Type: "bool"}, + {Code: "needExternalAddressIngress", Field: "NeedExternalAddressIngress", Type: "bool"}, + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "workersCount", Field: "WorkersCount", Type: "int64"}, + {Code: "workersSizingDisk", Field: "WorkersSizingDisk", Type: "int64"}, + {Code: "workersSizingPolicy", Field: "WorkersSizingPolicy", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sSthutrvalClusterModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *K8sSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *K8sSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_update_secrets_action.go b/generated/prod/go/150_k8s_sthutrval_cluster_update_secrets_action.go similarity index 94% rename from provider/internal/resources_gen/150_k8s_sthutrval_cluster_update_secrets_action.go rename to generated/prod/go/150_k8s_sthutrval_cluster_update_secrets_action.go index 387b4d8..abb4ff8 100644 --- a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_update_secrets_action.go +++ b/generated/prod/go/150_k8s_sthutrval_cluster_update_secrets_action.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -27,6 +28,7 @@ type K8sSthutrvalClusterUpdateSecretsModel struct { ID types.String `tfsdk:"id"` K8sSthutrvalClusterID types.String `tfsdk:"k8s_sthutrval_cluster_id"` RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` } @@ -43,6 +45,7 @@ func (r *K8sSthutrvalClusterUpdateSecretsResource) Schema(ctx context.Context, r "id": schema.StringAttribute{Computed: true}, "k8s_sthutrval_cluster_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, } resp.Schema = schema.Schema{Attributes: attrs} diff --git a/provider/internal/resources_gen/89_flask_reconcile_action.go b/generated/prod/go/19_vc_org_reconcile_action.go similarity index 71% rename from provider/internal/resources_gen/89_flask_reconcile_action.go rename to generated/prod/go/19_vc_org_reconcile_action.go index 11d6549..3e5dc0f 100644 --- a/provider/internal/resources_gen/89_flask_reconcile_action.go +++ b/generated/prod/go/19_vc_org_reconcile_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: flask +// Service: vc_org // Action: reconcile -var _ resource.Resource = &FlaskReconcileResource{} +var _ resource.Resource = &VcOrgReconcileResource{} -// FlaskReconcileResource triggers a one-time action. -type FlaskReconcileResource struct { +// VcOrgReconcileResource triggers a one-time action. +type VcOrgReconcileResource struct { client *core.UniversalClient } -type FlaskReconcileModel struct { +type VcOrgReconcileModel struct { ID types.String `tfsdk:"id"` - FlaskID types.String `tfsdk:"flask_id"` + VcOrgID types.String `tfsdk:"vc_org_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewFlaskReconcileResource() resource.Resource { - return &FlaskReconcileResource{} +func NewVcOrgReconcileResource() resource.Resource { + return &VcOrgReconcileResource{} } -func (r *FlaskReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_flask_reconcile" +func (r *VcOrgReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_org_reconcile" } -func (r *FlaskReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *VcOrgReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "flask_id": schema.StringAttribute{Required: true}, + "vc_org_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *FlaskReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan FlaskReconcileModel +func (r *VcOrgReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan VcOrgReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.FlaskID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,9 +71,6 @@ func (r *FlaskReconcileResource) Create(ctx context.Context, req resource.Create if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return @@ -85,8 +80,8 @@ func (r *FlaskReconcileResource) Create(ctx context.Context, req resource.Create resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *FlaskReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state FlaskReconcileModel +func (r *VcOrgReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcOrgReconcileModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *FlaskReconcileResource) Read(ctx context.Context, req resource.ReadRequ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *FlaskReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan FlaskReconcileModel +func (r *VcOrgReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcOrgReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.FlaskID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -119,11 +114,11 @@ func (r *FlaskReconcileResource) Update(ctx context.Context, req resource.Update resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *FlaskReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *VcOrgReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *FlaskReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *VcOrgReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/19_vc_org_resource.go b/generated/prod/go/19_vc_org_resource.go new file mode 100644 index 0000000..c070e03 --- /dev/null +++ b/generated/prod/go/19_vc_org_resource.go @@ -0,0 +1,372 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_org +// Service ID: 19 + +var _ resource.Resource = &VcOrgResource{} +var _ resource.ResourceWithModifyPlan = &VcOrgResource{} +var _ resource.ResourceWithImportState = &VcOrgResource{} + +type VcOrgResource struct { + client *core.UniversalClient +} + +type VcOrgModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + OrganizationType types.String `tfsdk:"organization_type"` + ResourceRealm types.String `tfsdk:"resource_realm"` + VIPConfigure types.String `tfsdk:"v_i_p_configure"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcOrgResource() resource.Resource { + return &VcOrgResource{} +} + +func (r *VcOrgResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_org" +} + +func (r *VcOrgResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "organization_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("iaas"), MarkdownDescription: "Доступ к организации предоставляется только при выборе `iaas` Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Облако в котором будет развёрнут тенант Не изменяется после создания."}, + "v_i_p_configure": schema.StringAttribute{Required: true}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcOrgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcOrgModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcOrgModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcOrgModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.OrganizationType.IsNull() && !plan.OrganizationType.IsUnknown() && !state.OrganizationType.IsNull() && !state.OrganizationType.IsUnknown() { + if plan.OrganizationType.ValueString() != state.OrganizationType.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить organization_type", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.VIPConfigure.IsNull() || config.VIPConfigure.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 418: resources_core.FormatString(config.ResourceRealm), + 556: resources_core.FormatString(config.OrganizationType), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 19, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcOrgResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcOrgModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.VIPConfigure.IsNull() || data.VIPConfigure.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 418: resources_core.FormatString(data.ResourceRealm), + 556: resources_core.FormatString(data.OrganizationType), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 19, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "organizationType", Field: "OrganizationType", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "vIPConfigure", Field: "VIPConfigure", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcOrgResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcOrgModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 19, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "organizationType", Field: "OrganizationType", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "vIPConfigure", Field: "VIPConfigure", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcOrgModel + var state VcOrgModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.VIPConfigure.IsNull() != state.VIPConfigure.IsNull() || plan.VIPConfigure.IsUnknown() != state.VIPConfigure.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VIPConfigure.IsNull() && !plan.VIPConfigure.IsUnknown() && !state.VIPConfigure.IsNull() && !state.VIPConfigure.IsUnknown() { + if plan.VIPConfigure.ValueString() != state.VIPConfigure.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 662: resources_core.FormatString(plan.VIPConfigure), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 19, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "organizationType", Field: "OrganizationType", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "vIPConfigure", Field: "VIPConfigure", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcOrgResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcOrgModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcOrgResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcOrgResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/98_http_reconcile_action.go b/generated/prod/go/1_dummy_redeploy_action.go similarity index 67% rename from provider/internal/resources_gen/98_http_reconcile_action.go rename to generated/prod/go/1_dummy_redeploy_action.go index 9945ea5..aaf8c7f 100644 --- a/provider/internal/resources_gen/98_http_reconcile_action.go +++ b/generated/prod/go/1_dummy_redeploy_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: http -// Action: reconcile +// Service: dummy +// Action: redeploy -var _ resource.Resource = &HttpReconcileResource{} +var _ resource.Resource = &DummyRedeployResource{} -// HttpReconcileResource triggers a one-time action. -type HttpReconcileResource struct { +// DummyRedeployResource triggers a one-time action. +type DummyRedeployResource struct { client *core.UniversalClient } -type HttpReconcileModel struct { +type DummyRedeployModel struct { ID types.String `tfsdk:"id"` - HttpID types.String `tfsdk:"http_id"` + DummyID types.String `tfsdk:"dummy_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewHttpReconcileResource() resource.Resource { - return &HttpReconcileResource{} +func NewDummyRedeployResource() resource.Resource { + return &DummyRedeployResource{} } -func (r *HttpReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_http_reconcile" +func (r *DummyRedeployResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dummy_redeploy" } -func (r *HttpReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *DummyRedeployResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "http_id": schema.StringAttribute{Required: true}, + "dummy_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *HttpReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan HttpReconcileModel +func (r *DummyRedeployResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan DummyRedeployModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.HttpID.ValueString()) + instanceUID := strings.TrimSpace(plan.DummyID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,20 +71,17 @@ func (r *HttpReconcileResource) Create(ctx context.Context, req resource.CreateR if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "redeploy", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *HttpReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HttpReconcileModel +func (r *DummyRedeployResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DummyRedeployModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *HttpReconcileResource) Read(ctx context.Context, req resource.ReadReque resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *HttpReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HttpReconcileModel +func (r *DummyRedeployResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan DummyRedeployModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.HttpID.ValueString()) + instanceUID := strings.TrimSpace(plan.DummyID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -110,20 +105,20 @@ func (r *HttpReconcileResource) Update(ctx context.Context, req resource.UpdateR params := map[string]string{} - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *HttpReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *DummyRedeployResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *HttpReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *DummyRedeployResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/1_dummy_resource.go b/generated/prod/go/1_dummy_resource.go new file mode 100644 index 0000000..2a24798 --- /dev/null +++ b/generated/prod/go/1_dummy_resource.go @@ -0,0 +1,507 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: dummy +// Service ID: 1 + +var _ resource.Resource = &DummyResource{} +var _ resource.ResourceWithModifyPlan = &DummyResource{} +var _ resource.ResourceWithImportState = &DummyResource{} + +type DummyResource struct { + client *core.UniversalClient +} + +type DummyModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ArrayMapFixedExample types.String `tfsdk:"array_map_fixed_example"` + Bodymessage types.String `tfsdk:"bodymessage"` + DurationMs types.Int64 `tfsdk:"duration_ms"` + FailAtStart types.Bool `tfsdk:"fail_at_start"` + FailInProgress types.Bool `tfsdk:"fail_in_progress"` + JsonExample types.String `tfsdk:"json_example"` + MapExample types.String `tfsdk:"map_example"` + MapFixed types.String `tfsdk:"map_fixed"` + NestedRefExample types.String `tfsdk:"nested_ref_example"` + ResourceRealm types.String `tfsdk:"resource_realm"` + WhereFail types.Int64 `tfsdk:"where_fail"` + YamlExample types.String `tfsdk:"yaml_example"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewDummyResource() resource.Resource { + return &DummyResource{} +} + +func (r *DummyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dummy" +} + +func (r *DummyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "array_map_fixed_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Array of map-fixed Не изменяется после создания."}, + "bodymessage": schema.StringAttribute{Optional: true, MarkdownDescription: "Дата, уходящая в Vault"}, + "duration_ms": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "перед завершением подождать (sleep) столько миллисекунд"}, + "fail_at_start": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, завершить джоб, ничего не записывая в cmdb"}, + "fail_in_progress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb"}, + "json_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Для тестирования интерфейса"}, + "map_example": schema.StringAttribute{Optional: true}, + "map_fixed": schema.StringAttribute{Optional: true, MarkdownDescription: "Новый тип поля Не изменяется после создания."}, + "nested_ref_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "where_fail": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write"}, + "yaml_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *DummyModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *DummyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan DummyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ArrayMapFixedExample.IsNull() && !plan.ArrayMapFixedExample.IsUnknown() && !state.ArrayMapFixedExample.IsNull() && !state.ArrayMapFixedExample.IsUnknown() { + if plan.ArrayMapFixedExample.ValueString() != state.ArrayMapFixedExample.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить array_map_fixed_example", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.MapFixed.IsNull() && !plan.MapFixed.IsUnknown() && !state.MapFixed.IsNull() && !state.MapFixed.IsUnknown() { + if plan.MapFixed.ValueString() != state.MapFixed.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить map_fixed", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NestedRefExample.IsNull() && !plan.NestedRefExample.IsUnknown() && !state.NestedRefExample.IsNull() && !state.NestedRefExample.IsUnknown() { + if plan.NestedRefExample.ValueString() != state.NestedRefExample.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить nested_ref_example", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.YamlExample.IsNull() && !plan.YamlExample.IsUnknown() && !state.YamlExample.IsNull() && !state.YamlExample.IsUnknown() { + if plan.YamlExample.ValueString() != state.YamlExample.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить yaml_example", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 198: resources_core.FormatInt64(config.DurationMs), + 199: resources_core.FormatBool(config.FailAtStart), + 200: resources_core.FormatBool(config.FailInProgress), + 201: resources_core.FormatInt64(config.WhereFail), + 242: resources_core.FormatString(config.ResourceRealm), + 286: resources_core.FormatString(config.Bodymessage), + 321: resources_core.FormatString(config.MapExample), + 322: resources_core.FormatString(config.JsonExample), + 396: resources_core.FormatString(config.NestedRefExample), + 447: resources_core.FormatString(config.YamlExample), + 647: resources_core.FormatString(config.MapFixed), + 654: resources_core.FormatString(config.ArrayMapFixedExample), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 1, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data DummyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 198: resources_core.FormatInt64(data.DurationMs), + 199: resources_core.FormatBool(data.FailAtStart), + 200: resources_core.FormatBool(data.FailInProgress), + 201: resources_core.FormatInt64(data.WhereFail), + 242: resources_core.FormatString(data.ResourceRealm), + 286: resources_core.FormatString(data.Bodymessage), + 321: resources_core.FormatString(data.MapExample), + 322: resources_core.FormatString(data.JsonExample), + 396: resources_core.FormatString(data.NestedRefExample), + 447: resources_core.FormatString(data.YamlExample), + 647: resources_core.FormatString(data.MapFixed), + 654: resources_core.FormatString(data.ArrayMapFixedExample), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 1, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, + {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, + {Code: "durationMs", Field: "DurationMs", Type: "int64"}, + {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, + {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, + {Code: "jsonExample", Field: "JsonExample", Type: "string"}, + {Code: "mapExample", Field: "MapExample", Type: "string"}, + {Code: "mapFixed", Field: "MapFixed", Type: "string"}, + {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + {Code: "yamlExample", Field: "YamlExample", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DummyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 1, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, + {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, + {Code: "durationMs", Field: "DurationMs", Type: "int64"}, + {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, + {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, + {Code: "jsonExample", Field: "JsonExample", Type: "string"}, + {Code: "mapExample", Field: "MapExample", Type: "string"}, + {Code: "mapFixed", Field: "MapFixed", Type: "string"}, + {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + {Code: "yamlExample", Field: "YamlExample", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan DummyModel + var state DummyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.DurationMs.IsNull() != state.DurationMs.IsNull() || plan.DurationMs.IsUnknown() != state.DurationMs.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.DurationMs.IsNull() && !plan.DurationMs.IsUnknown() && !state.DurationMs.IsNull() && !state.DurationMs.IsUnknown() { + if plan.DurationMs.ValueInt64() != state.DurationMs.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.FailAtStart.IsNull() != state.FailAtStart.IsNull() || plan.FailAtStart.IsUnknown() != state.FailAtStart.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.FailAtStart.IsNull() && !plan.FailAtStart.IsUnknown() && !state.FailAtStart.IsNull() && !state.FailAtStart.IsUnknown() { + if plan.FailAtStart.ValueBool() != state.FailAtStart.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.FailInProgress.IsNull() != state.FailInProgress.IsNull() || plan.FailInProgress.IsUnknown() != state.FailInProgress.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.FailInProgress.IsNull() && !plan.FailInProgress.IsUnknown() && !state.FailInProgress.IsNull() && !state.FailInProgress.IsUnknown() { + if plan.FailInProgress.ValueBool() != state.FailInProgress.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.WhereFail.IsNull() != state.WhereFail.IsNull() || plan.WhereFail.IsUnknown() != state.WhereFail.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.WhereFail.IsNull() && !plan.WhereFail.IsUnknown() && !state.WhereFail.IsNull() && !state.WhereFail.IsUnknown() { + if plan.WhereFail.ValueInt64() != state.WhereFail.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.Bodymessage.IsNull() != state.Bodymessage.IsNull() || plan.Bodymessage.IsUnknown() != state.Bodymessage.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.Bodymessage.IsNull() && !plan.Bodymessage.IsUnknown() && !state.Bodymessage.IsNull() && !state.Bodymessage.IsUnknown() { + if plan.Bodymessage.ValueString() != state.Bodymessage.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MapExample.IsNull() != state.MapExample.IsNull() || plan.MapExample.IsUnknown() != state.MapExample.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MapExample.IsNull() && !plan.MapExample.IsUnknown() && !state.MapExample.IsNull() && !state.MapExample.IsUnknown() { + if plan.MapExample.ValueString() != state.MapExample.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonExample.IsNull() != state.JsonExample.IsNull() || plan.JsonExample.IsUnknown() != state.JsonExample.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonExample.IsNull() && !plan.JsonExample.IsUnknown() && !state.JsonExample.IsNull() && !state.JsonExample.IsUnknown() { + if plan.JsonExample.ValueString() != state.JsonExample.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 287: resources_core.FormatInt64(plan.DurationMs), + 288: resources_core.FormatBool(plan.FailAtStart), + 289: resources_core.FormatBool(plan.FailInProgress), + 290: resources_core.FormatInt64(plan.WhereFail), + 291: resources_core.FormatString(plan.Bodymessage), + 373: resources_core.FormatString(plan.MapExample), + 374: resources_core.FormatString(plan.JsonExample), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 1, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, + {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, + {Code: "durationMs", Field: "DurationMs", Type: "int64"}, + {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, + {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, + {Code: "jsonExample", Field: "JsonExample", Type: "string"}, + {Code: "mapExample", Field: "MapExample", Type: "string"}, + {Code: "mapFixed", Field: "MapFixed", Type: "string"}, + {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + {Code: "yamlExample", Field: "YamlExample", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DummyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state DummyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *DummyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *DummyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/20_vc_org_saas_resource.go b/generated/prod/go/20_vc_org_saas_resource.go new file mode 100644 index 0000000..7488f49 --- /dev/null +++ b/generated/prod/go/20_vc_org_saas_resource.go @@ -0,0 +1,334 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_org_saas +// Service ID: 20 + +var _ resource.Resource = &VcOrgSaasResource{} +var _ resource.ResourceWithModifyPlan = &VcOrgSaasResource{} +var _ resource.ResourceWithImportState = &VcOrgSaasResource{} + +type VcOrgSaasResource struct { + client *core.UniversalClient +} + +type VcOrgSaasModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcOrgSaasResource() resource.Resource { + return &VcOrgSaasResource{} +} + +func (r *VcOrgSaasResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_org_saas" +} + +func (r *VcOrgSaasResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcOrgSaasResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcOrgSaasModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcOrgSaasModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcOrgSaasModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 185: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 20, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcOrgSaasResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcOrgSaasModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 185: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 20, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcOrgSaasResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcOrgSaasModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 20, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcOrgSaasResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcOrgSaasModel + var state VcOrgSaasModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 20, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcOrgSaasResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcOrgSaasModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcOrgSaasResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcOrgSaasResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/94_lucee_reconcile_action.go b/generated/prod/go/21_vc_vdc_reconcile_action.go similarity index 71% rename from provider/internal/resources_gen/94_lucee_reconcile_action.go rename to generated/prod/go/21_vc_vdc_reconcile_action.go index 2b531f3..691819f 100644 --- a/provider/internal/resources_gen/94_lucee_reconcile_action.go +++ b/generated/prod/go/21_vc_vdc_reconcile_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: lucee +// Service: vc_vdc // Action: reconcile -var _ resource.Resource = &LuceeReconcileResource{} +var _ resource.Resource = &VcVdcReconcileResource{} -// LuceeReconcileResource triggers a one-time action. -type LuceeReconcileResource struct { +// VcVdcReconcileResource triggers a one-time action. +type VcVdcReconcileResource struct { client *core.UniversalClient } -type LuceeReconcileModel struct { +type VcVdcReconcileModel struct { ID types.String `tfsdk:"id"` - LuceeID types.String `tfsdk:"lucee_id"` + VcVdcID types.String `tfsdk:"vc_vdc_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewLuceeReconcileResource() resource.Resource { - return &LuceeReconcileResource{} +func NewVcVdcReconcileResource() resource.Resource { + return &VcVdcReconcileResource{} } -func (r *LuceeReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_lucee_reconcile" +func (r *VcVdcReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc_reconcile" } -func (r *LuceeReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *VcVdcReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "lucee_id": schema.StringAttribute{Required: true}, + "vc_vdc_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *LuceeReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LuceeReconcileModel +func (r *VcVdcReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan VcVdcReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.LuceeID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcVdcID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,9 +71,6 @@ func (r *LuceeReconcileResource) Create(ctx context.Context, req resource.Create if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return @@ -85,8 +80,8 @@ func (r *LuceeReconcileResource) Create(ctx context.Context, req resource.Create resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *LuceeReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LuceeReconcileModel +func (r *VcVdcReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcReconcileModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *LuceeReconcileResource) Read(ctx context.Context, req resource.ReadRequ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *LuceeReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LuceeReconcileModel +func (r *VcVdcReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.LuceeID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcVdcID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -119,11 +114,11 @@ func (r *LuceeReconcileResource) Update(ctx context.Context, req resource.Update resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *LuceeReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *VcVdcReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *LuceeReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *VcVdcReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/21_vc_vdc_resource.go b/generated/prod/go/21_vc_vdc_resource.go new file mode 100644 index 0000000..5838894 --- /dev/null +++ b/generated/prod/go/21_vc_vdc_resource.go @@ -0,0 +1,521 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vdc +// Service ID: 21 + +var _ resource.Resource = &VcVdcResource{} +var _ resource.ResourceWithModifyPlan = &VcVdcResource{} +var _ resource.ResourceWithImportState = &VcVdcResource{} + +type VcVdcResource struct { + client *core.UniversalClient +} + +type VcVdcModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + CpuAllocated types.Int64 `tfsdk:"cpu_allocated"` + CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` + MemAllocated types.Int64 `tfsdk:"mem_allocated"` + MemGuaranteed types.Int64 `tfsdk:"mem_guaranteed"` + NetworkProvider types.String `tfsdk:"network_provider"` + OrganizationUid types.String `tfsdk:"organization_uid"` + ProviderVdc types.String `tfsdk:"provider_vdc"` + StorageConfig types.String `tfsdk:"storage_config"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVdcResource() resource.Resource { + return &VcVdcResource{} +} + +func (r *VcVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc" +} + +func (r *VcVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "cpu_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "шт"}, + "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "% резервирования Не изменяется после создания."}, + "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "gb"}, + "mem_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "% резервирования Не изменяется после создания."}, + "network_provider": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "organization_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` Не изменяется после создания."}, + "provider_vdc": schema.StringAttribute{Required: true, MarkdownDescription: "Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
Не изменяется после создания."}, + "storage_config": schema.StringAttribute{Required: true, MarkdownDescription: "Первая политика в списке указывается как "По умолчанию""}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVdcResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVdcModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, plan.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != plan.OrganizationUid.ValueString() { + plan.OrganizationUid = types.StringValue(resolvedOrganizationUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { + if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.MemGuaranteed.IsNull() && !plan.MemGuaranteed.IsUnknown() && !state.MemGuaranteed.IsNull() && !state.MemGuaranteed.IsUnknown() { + if plan.MemGuaranteed.ValueInt64() != state.MemGuaranteed.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить mem_guaranteed", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NetworkProvider.IsNull() && !plan.NetworkProvider.IsUnknown() && !state.NetworkProvider.IsNull() && !state.NetworkProvider.IsUnknown() { + if plan.NetworkProvider.ValueString() != state.NetworkProvider.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить network_provider", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { + if plan.OrganizationUid.ValueString() != state.OrganizationUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить organization_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ProviderVdc.IsNull() && !plan.ProviderVdc.IsUnknown() && !state.ProviderVdc.IsNull() && !state.ProviderVdc.IsUnknown() { + if plan.ProviderVdc.ValueString() != state.ProviderVdc.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить provider_vdc", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.NetworkProvider.IsNull() || config.NetworkProvider.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") + return + } + if config.OrganizationUid.IsNull() || config.OrganizationUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") + return + } + if config.ProviderVdc.IsNull() || config.ProviderVdc.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") + return + } + if config.StorageConfig.IsNull() || config.StorageConfig.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "storage_config is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.OrganizationUid.IsNull() && !config.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, config.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != config.OrganizationUid.ValueString() { + config.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + params := map[int]string{ + 30: resources_core.FormatString(config.OrganizationUid), + 335: resources_core.FormatString(config.ProviderVdc), + 361: resources_core.FormatString(config.StorageConfig), + 397: resources_core.FormatInt64(config.CpuGuaranteed), + 398: resources_core.FormatInt64(config.MemGuaranteed), + 557: resources_core.FormatInt64(config.CpuAllocated), + 558: resources_core.FormatInt64(config.MemAllocated), + 746: resources_core.FormatString(config.NetworkProvider), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 21, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.NetworkProvider.IsNull() || data.NetworkProvider.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") + return + } + if data.OrganizationUid.IsNull() || data.OrganizationUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") + return + } + if data.ProviderVdc.IsNull() || data.ProviderVdc.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") + return + } + if data.StorageConfig.IsNull() || data.StorageConfig.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "storage_config is required.") + return + } + if !data.OrganizationUid.IsNull() && !data.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, data.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedOrganizationUid != "" && resolvedOrganizationUid != data.OrganizationUid.ValueString() { + data.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 30: resources_core.FormatString(data.OrganizationUid), + 335: resources_core.FormatString(data.ProviderVdc), + 361: resources_core.FormatString(data.StorageConfig), + 397: resources_core.FormatInt64(data.CpuGuaranteed), + 398: resources_core.FormatInt64(data.MemGuaranteed), + 557: resources_core.FormatInt64(data.CpuAllocated), + 558: resources_core.FormatInt64(data.MemAllocated), + 746: resources_core.FormatString(data.NetworkProvider), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 21, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, + {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, + {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, + {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { + state.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 21, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, + {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, + {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, + {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, newState.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != newState.OrganizationUid.ValueString() { + newState.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcModel + var state VcVdcModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.CpuAllocated.IsNull() != state.CpuAllocated.IsNull() || plan.CpuAllocated.IsUnknown() != state.CpuAllocated.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { + if plan.CpuAllocated.ValueInt64() != state.CpuAllocated.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MemAllocated.IsNull() != state.MemAllocated.IsNull() || plan.MemAllocated.IsUnknown() != state.MemAllocated.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { + if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.StorageConfig.IsNull() != state.StorageConfig.IsNull() || plan.StorageConfig.IsUnknown() != state.StorageConfig.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { + if plan.StorageConfig.ValueString() != state.StorageConfig.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 560: resources_core.FormatInt64(plan.CpuAllocated), + 561: resources_core.FormatInt64(plan.MemAllocated), + 562: resources_core.FormatString(plan.StorageConfig), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 21, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, + {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, + {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, + {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, + {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, + {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, + {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, + {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { + resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) + } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { + state.OrganizationUid = types.StringValue(resolvedOrganizationUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVdcModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVdcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/95_nodejs_reconcile_action.go b/generated/prod/go/22_vc_nsxt_reconcile_action.go similarity index 71% rename from provider/internal/resources_gen/95_nodejs_reconcile_action.go rename to generated/prod/go/22_vc_nsxt_reconcile_action.go index 90ff262..f9ed455 100644 --- a/provider/internal/resources_gen/95_nodejs_reconcile_action.go +++ b/generated/prod/go/22_vc_nsxt_reconcile_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodejs +// Service: vc_nsxt // Action: reconcile -var _ resource.Resource = &NodejsReconcileResource{} +var _ resource.Resource = &VcNsxtReconcileResource{} -// NodejsReconcileResource triggers a one-time action. -type NodejsReconcileResource struct { +// VcNsxtReconcileResource triggers a one-time action. +type VcNsxtReconcileResource struct { client *core.UniversalClient } -type NodejsReconcileModel struct { +type VcNsxtReconcileModel struct { ID types.String `tfsdk:"id"` - NodejsID types.String `tfsdk:"nodejs_id"` + VcNsxtID types.String `tfsdk:"vc_nsxt_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewNodejsReconcileResource() resource.Resource { - return &NodejsReconcileResource{} +func NewVcNsxtReconcileResource() resource.Resource { + return &VcNsxtReconcileResource{} } -func (r *NodejsReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodejs_reconcile" +func (r *VcNsxtReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_nsxt_reconcile" } -func (r *NodejsReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *VcNsxtReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "nodejs_id": schema.StringAttribute{Required: true}, + "vc_nsxt_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *NodejsReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan NodejsReconcileModel +func (r *VcNsxtReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan VcNsxtReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.NodejsID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcNsxtID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,9 +71,6 @@ func (r *NodejsReconcileResource) Create(ctx context.Context, req resource.Creat if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return @@ -85,8 +80,8 @@ func (r *NodejsReconcileResource) Create(ctx context.Context, req resource.Creat resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *NodejsReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NodejsReconcileModel +func (r *VcNsxtReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcNsxtReconcileModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *NodejsReconcileResource) Read(ctx context.Context, req resource.ReadReq resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *NodejsReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NodejsReconcileModel +func (r *VcNsxtReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcNsxtReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.NodejsID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcNsxtID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -119,11 +114,11 @@ func (r *NodejsReconcileResource) Update(ctx context.Context, req resource.Updat resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *NodejsReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *VcNsxtReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *NodejsReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *VcNsxtReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/22_vc_nsxt_resource.go b/generated/prod/go/22_vc_nsxt_resource.go new file mode 100644 index 0000000..8580ab1 --- /dev/null +++ b/generated/prod/go/22_vc_nsxt_resource.go @@ -0,0 +1,525 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_nsxt +// Service ID: 22 + +var _ resource.Resource = &VcNsxtResource{} +var _ resource.ResourceWithModifyPlan = &VcNsxtResource{} +var _ resource.ResourceWithImportState = &VcNsxtResource{} + +type VcNsxtResource struct { + client *core.UniversalClient +} + +type VcNsxtModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` + NeedExternalAddressSNAT types.Bool `tfsdk:"need_external_address_s_n_a_t"` + VdcGroupUid types.String `tfsdk:"vdc_group_uid"` + VdcType types.String `tfsdk:"vdc_type"` + VdcUid types.String `tfsdk:"vdc_uid"` + VirtualServicesCount types.Int64 `tfsdk:"virtual_services_count"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcNsxtResource() resource.Resource { + return &VcNsxtResource{} +} + +func (r *VcNsxtResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_nsxt" +} + +func (r *VcNsxtResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ip Space для внешнего IP

getKeyListFromStruct(getInstanceState(getValueFromStruct(getInstanceState(getValueFromStruct(getInstanceState(), "params.vdcUid")), "params.organizationUid")), "params.vIPConfigure")"}, + "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включение Load Balancer"}, + "need_external_address_s_n_a_t": schema.BoolAttribute{Optional: true, MarkdownDescription: "Создать правило SNAT на подсеть `10.10.102.0/24`"}, + "vdc_group_uid": schema.StringAttribute{Optional: true, MarkdownDescription: "UUID Услуги `Группа виртуальных датацентров (groupvDC)` Не изменяется после создания."}, + "vdc_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vdc"), MarkdownDescription: "Тип родительской услуги Не изменяется после создания."}, + "vdc_uid": schema.StringAttribute{Optional: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, + "virtual_services_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Кол-во виртуальных сервисов, которые выделяются на AVI"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcNsxtResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcNsxtModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcNsxtModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcNsxtModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, plan.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != plan.VdcGroupUid.ValueString() { + plan.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + planChanged = true + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { + if plan.VdcGroupUid.ValueString() != state.VdcGroupUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_group_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcType.IsNull() && !plan.VdcType.IsUnknown() && !state.VdcType.IsNull() && !state.VdcType.IsUnknown() { + if plan.VdcType.ValueString() != state.VdcType.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_type", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.VdcGroupUid.IsNull() && !config.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, config.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != config.VdcGroupUid.ValueString() { + config.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + params := map[int]string{ + 8: resources_core.FormatString(config.VdcUid), + 340: resources_core.FormatBool(config.NeedEnableAVI), + 341: resources_core.FormatInt64(config.VirtualServicesCount), + 621: resources_core.FormatString(config.VdcType), + 622: resources_core.FormatString(config.VdcGroupUid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 22, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcNsxtResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcNsxtModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if !data.VdcGroupUid.IsNull() && !data.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, data.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != data.VdcGroupUid.ValueString() { + data.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 8: resources_core.FormatString(data.VdcUid), + 340: resources_core.FormatBool(data.NeedEnableAVI), + 341: resources_core.FormatInt64(data.VirtualServicesCount), + 621: resources_core.FormatString(data.VdcType), + 622: resources_core.FormatString(data.VdcGroupUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 22, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, + {Code: "vdcType", Field: "VdcType", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { + state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcNsxtResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcNsxtModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 22, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, + {Code: "vdcType", Field: "VdcType", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, newState.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != newState.VdcGroupUid.ValueString() { + newState.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcNsxtModel + var state VcNsxtModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.NeedEnableAVI.IsNull() != state.NeedEnableAVI.IsNull() || plan.NeedEnableAVI.IsUnknown() != state.NeedEnableAVI.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { + if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VirtualServicesCount.IsNull() != state.VirtualServicesCount.IsNull() || plan.VirtualServicesCount.IsUnknown() != state.VirtualServicesCount.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { + if plan.VirtualServicesCount.ValueInt64() != state.VirtualServicesCount.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressSNAT.IsNull() != state.NeedExternalAddressSNAT.IsNull() || plan.NeedExternalAddressSNAT.IsUnknown() != state.NeedExternalAddressSNAT.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressSNAT.IsNull() && !plan.NeedExternalAddressSNAT.IsUnknown() && !state.NeedExternalAddressSNAT.IsNull() && !state.NeedExternalAddressSNAT.IsUnknown() { + if plan.NeedExternalAddressSNAT.ValueBool() != state.NeedExternalAddressSNAT.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 368: resources_core.FormatBool(plan.NeedEnableAVI), + 369: resources_core.FormatInt64(plan.VirtualServicesCount), + 371: resources_core.FormatBool(plan.NeedExternalAddressSNAT), + 372: resources_core.FormatString(plan.IpSpaceName), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 22, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, + {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, + {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, + {Code: "vdcType", Field: "VdcType", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { + resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) + } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { + state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcNsxtResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcNsxtModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcNsxtResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcNsxtResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/23_vc_vm_resource.go b/generated/prod/go/23_vc_vm_resource.go new file mode 100644 index 0000000..c891163 --- /dev/null +++ b/generated/prod/go/23_vc_vm_resource.go @@ -0,0 +1,509 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vm +// Service ID: 23 + +var _ resource.Resource = &VcVmResource{} +var _ resource.ResourceWithModifyPlan = &VcVmResource{} +var _ resource.ResourceWithImportState = &VcVmResource{} + +type VcVmResource struct { + client *core.UniversalClient +} + +type VcVmModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AccessIpList types.String `tfsdk:"access_ip_list"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + TemplateName types.String `tfsdk:"template_name"` + VappUid types.String `tfsdk:"vapp_uid"` + VmCpu types.String `tfsdk:"vm_cpu"` + VmDisksize types.String `tfsdk:"vm_disksize"` + VmName types.String `tfsdk:"vm_name"` + VmRam types.String `tfsdk:"vm_ram"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVmResource() resource.Resource { + return &VcVmResource{} +} + +func (r *VcVmResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vm" +} + +func (r *VcVmResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("79.111.119.78/32 185.247.187.1/32"), MarkdownDescription: "Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов"}, + "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) Не изменяется после создания."}, + "template_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("RockyLinux_9-16G"), MarkdownDescription: "ОС Не изменяется после создания."}, + "vapp_uid": schema.StringAttribute{Required: true, MarkdownDescription: "InstanceUid vApp. vApp должен быть создан Не изменяется после создания."}, + "vm_cpu": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "CPU"}, + "vm_disksize": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("10"), MarkdownDescription: "Размер диска под сервис (GB)"}, + "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vm0001"), MarkdownDescription: "Имя ВМ. Не поддерживает спец-символы Не изменяется после создания."}, + "vm_ram": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "RAM (GB)"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVmResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVmModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVmModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVmModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, plan.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != plan.VappUid.ValueString() { + plan.VappUid = types.StringValue(resolvedVappUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.TemplateName.IsNull() && !plan.TemplateName.IsUnknown() && !state.TemplateName.IsNull() && !state.TemplateName.IsUnknown() { + if plan.TemplateName.ValueString() != state.TemplateName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить template_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + if plan.VappUid.ValueString() != state.VappUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { + if plan.VmName.ValueString() != state.VmName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.IpSpaceName.IsNull() || config.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if config.VappUid.IsNull() || config.VappUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { + config.VappUid = types.StringValue(resolvedVappUid) + } + } + params := map[int]string{ + 33: resources_core.FormatString(config.VappUid), + 35: resources_core.FormatString(config.VmName), + 36: resources_core.FormatString(config.VmCpu), + 37: resources_core.FormatString(config.VmRam), + 38: resources_core.FormatString(config.VmDisksize), + 176: resources_core.FormatString(config.TemplateName), + 177: resources_core.FormatString(config.AccessIpList), + 406: resources_core.FormatString(config.IpSpaceName), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 23, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVmResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVmModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if data.VappUid.IsNull() || data.VappUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") + return + } + if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { + data.VappUid = types.StringValue(resolvedVappUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 33: resources_core.FormatString(data.VappUid), + 35: resources_core.FormatString(data.VmName), + 36: resources_core.FormatString(data.VmCpu), + 37: resources_core.FormatString(data.VmRam), + 38: resources_core.FormatString(data.VmDisksize), + 176: resources_core.FormatString(data.TemplateName), + 177: resources_core.FormatString(data.AccessIpList), + 406: resources_core.FormatString(data.IpSpaceName), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 23, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "templateName", Field: "TemplateName", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "string"}, + {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { + state.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVmModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 23, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "templateName", Field: "TemplateName", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "string"}, + {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { + newState.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVmResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVmModel + var state VcVmModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { + if plan.VmCpu.ValueString() != state.VmCpu.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { + if plan.VmRam.ValueString() != state.VmRam.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VmDisksize.IsNull() != state.VmDisksize.IsNull() || plan.VmDisksize.IsUnknown() != state.VmDisksize.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmDisksize.IsNull() && !plan.VmDisksize.IsUnknown() && !state.VmDisksize.IsNull() && !state.VmDisksize.IsUnknown() { + if plan.VmDisksize.ValueString() != state.VmDisksize.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { + if plan.AccessIpList.ValueString() != state.AccessIpList.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 186: resources_core.FormatString(plan.VmCpu), + 187: resources_core.FormatString(plan.VmRam), + 188: resources_core.FormatString(plan.VmDisksize), + 189: resources_core.FormatString(plan.AccessIpList), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 23, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "templateName", Field: "TemplateName", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "string"}, + {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { + state.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVmModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVmResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVmResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/25_vcexternalip_resource.go b/generated/prod/go/25_vcexternalip_resource.go new file mode 100644 index 0000000..5f89219 --- /dev/null +++ b/generated/prod/go/25_vcexternalip_resource.go @@ -0,0 +1,558 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vcexternalip +// Service ID: 25 + +var _ resource.Resource = &VcexternalipResource{} +var _ resource.ResourceWithModifyPlan = &VcexternalipResource{} +var _ resource.ResourceWithImportState = &VcexternalipResource{} + +type VcexternalipResource struct { + client *core.UniversalClient +} + +type VcexternalipModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + DnatCreate types.Bool `tfsdk:"dnat_create"` + FromServiceCloudEdgeName types.String `tfsdk:"from_service_cloud_edge_name"` + FromServiceCloudEdgeScope types.String `tfsdk:"from_service_cloud_edge_scope"` + FromServiceCloudOrgName types.String `tfsdk:"from_service_cloud_org_name"` + FromServiceCloudVdcName types.String `tfsdk:"from_service_cloud_vdc_name"` + FromServiceCloudVmwareUrl types.String `tfsdk:"from_service_cloud_vmware_url"` + FromServiceNamespace types.String `tfsdk:"from_service_namespace"` + FromServiceVdcGroupName types.String `tfsdk:"from_service_vdc_group_name"` + InternalAddrAccess types.String `tfsdk:"internal_addr_access"` + InternalPortAccess types.String `tfsdk:"internal_port_access"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + ResourceRealm types.String `tfsdk:"resource_realm"` + ServiceUid types.String `tfsdk:"service_uid"` + SnatCreate types.Bool `tfsdk:"snat_create"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcexternalipResource() resource.Resource { + return &VcexternalipResource{} +} + +func (r *VcexternalipResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vcexternalip" +} + +func (r *VcexternalipResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "dnat_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходимо создать DNAT-правило."}, + "from_service_cloud_edge_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_cloud_edge_scope": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, + "from_service_cloud_org_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Org составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_cloud_vdc_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_cloud_vmware_url": schema.StringAttribute{Optional: true, MarkdownDescription: "Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, + "from_service_namespace": schema.StringAttribute{Required: true, MarkdownDescription: "Namespace составной услуги (для отображения правил FW) Не изменяется после создания."}, + "from_service_vdc_group_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, + "internal_addr_access": schema.StringAttribute{Required: true, MarkdownDescription: "Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true"}, + "internal_port_access": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5432"), MarkdownDescription: "Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
"}, + "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "service_uid": schema.StringAttribute{Required: true}, + "snat_create": schema.BoolAttribute{Optional: true, MarkdownDescription: "Необходимо создать SNAT-правило.
"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcexternalipResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcexternalipModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcexternalipModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcexternalipModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.FromServiceCloudEdgeName.IsNull() && !plan.FromServiceCloudEdgeName.IsUnknown() && !state.FromServiceCloudEdgeName.IsNull() && !state.FromServiceCloudEdgeName.IsUnknown() { + if plan.FromServiceCloudEdgeName.ValueString() != state.FromServiceCloudEdgeName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudEdgeScope.IsNull() && !plan.FromServiceCloudEdgeScope.IsUnknown() && !state.FromServiceCloudEdgeScope.IsNull() && !state.FromServiceCloudEdgeScope.IsUnknown() { + if plan.FromServiceCloudEdgeScope.ValueString() != state.FromServiceCloudEdgeScope.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_scope", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudOrgName.IsNull() && !plan.FromServiceCloudOrgName.IsUnknown() && !state.FromServiceCloudOrgName.IsNull() && !state.FromServiceCloudOrgName.IsUnknown() { + if plan.FromServiceCloudOrgName.ValueString() != state.FromServiceCloudOrgName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_org_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudVdcName.IsNull() && !plan.FromServiceCloudVdcName.IsUnknown() && !state.FromServiceCloudVdcName.IsNull() && !state.FromServiceCloudVdcName.IsUnknown() { + if plan.FromServiceCloudVdcName.ValueString() != state.FromServiceCloudVdcName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vdc_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceCloudVmwareUrl.IsNull() && !plan.FromServiceCloudVmwareUrl.IsUnknown() && !state.FromServiceCloudVmwareUrl.IsNull() && !state.FromServiceCloudVmwareUrl.IsUnknown() { + if plan.FromServiceCloudVmwareUrl.ValueString() != state.FromServiceCloudVmwareUrl.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vmware_url", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceNamespace.IsNull() && !plan.FromServiceNamespace.IsUnknown() && !state.FromServiceNamespace.IsNull() && !state.FromServiceNamespace.IsUnknown() { + if plan.FromServiceNamespace.ValueString() != state.FromServiceNamespace.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_namespace", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.FromServiceVdcGroupName.IsNull() && !plan.FromServiceVdcGroupName.IsUnknown() && !state.FromServiceVdcGroupName.IsNull() && !state.FromServiceVdcGroupName.IsUnknown() { + if plan.FromServiceVdcGroupName.ValueString() != state.FromServiceVdcGroupName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить from_service_vdc_group_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.FromServiceNamespace.IsNull() || config.FromServiceNamespace.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") + return + } + if config.InternalAddrAccess.IsNull() || config.InternalAddrAccess.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") + return + } + if config.IpSpaceName.IsNull() || config.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ServiceUid.IsNull() || config.ServiceUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 140: resources_core.FormatString(config.ServiceUid), + 141: resources_core.FormatBool(config.DnatCreate), + 142: resources_core.FormatString(config.InternalPortAccess), + 143: resources_core.FormatBool(config.SnatCreate), + 144: resources_core.FormatString(config.InternalAddrAccess), + 324: resources_core.FormatString(config.FromServiceNamespace), + 325: resources_core.FormatString(config.FromServiceCloudEdgeName), + 326: resources_core.FormatString(config.FromServiceCloudVdcName), + 327: resources_core.FormatString(config.FromServiceCloudOrgName), + 328: resources_core.FormatString(config.FromServiceCloudVmwareUrl), + 336: resources_core.FormatString(config.IpSpaceName), + 488: resources_core.FormatString(config.ResourceRealm), + 624: resources_core.FormatString(config.FromServiceCloudEdgeScope), + 626: resources_core.FormatString(config.FromServiceVdcGroupName), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 25, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcexternalipResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcexternalipModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.FromServiceNamespace.IsNull() || data.FromServiceNamespace.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") + return + } + if data.InternalAddrAccess.IsNull() || data.InternalAddrAccess.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") + return + } + if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.ServiceUid.IsNull() || data.ServiceUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 140: resources_core.FormatString(data.ServiceUid), + 141: resources_core.FormatBool(data.DnatCreate), + 142: resources_core.FormatString(data.InternalPortAccess), + 143: resources_core.FormatBool(data.SnatCreate), + 144: resources_core.FormatString(data.InternalAddrAccess), + 324: resources_core.FormatString(data.FromServiceNamespace), + 325: resources_core.FormatString(data.FromServiceCloudEdgeName), + 326: resources_core.FormatString(data.FromServiceCloudVdcName), + 327: resources_core.FormatString(data.FromServiceCloudOrgName), + 328: resources_core.FormatString(data.FromServiceCloudVmwareUrl), + 336: resources_core.FormatString(data.IpSpaceName), + 488: resources_core.FormatString(data.ResourceRealm), + 624: resources_core.FormatString(data.FromServiceCloudEdgeScope), + 626: resources_core.FormatString(data.FromServiceVdcGroupName), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 25, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, + {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, + {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, + {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, + {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, + {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, + {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, + {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, + {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, + {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, + {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcexternalipResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcexternalipModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 25, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, + {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, + {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, + {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, + {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, + {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, + {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, + {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, + {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, + {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, + {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcexternalipModel + var state VcexternalipModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ServiceUid.IsNull() != state.ServiceUid.IsNull() || plan.ServiceUid.IsUnknown() != state.ServiceUid.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ServiceUid.IsNull() && !plan.ServiceUid.IsUnknown() && !state.ServiceUid.IsNull() && !state.ServiceUid.IsUnknown() { + if plan.ServiceUid.ValueString() != state.ServiceUid.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.DnatCreate.IsNull() != state.DnatCreate.IsNull() || plan.DnatCreate.IsUnknown() != state.DnatCreate.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.DnatCreate.IsNull() && !plan.DnatCreate.IsUnknown() && !state.DnatCreate.IsNull() && !state.DnatCreate.IsUnknown() { + if plan.DnatCreate.ValueBool() != state.DnatCreate.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.SnatCreate.IsNull() != state.SnatCreate.IsNull() || plan.SnatCreate.IsUnknown() != state.SnatCreate.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.SnatCreate.IsNull() && !plan.SnatCreate.IsUnknown() && !state.SnatCreate.IsNull() && !state.SnatCreate.IsUnknown() { + if plan.SnatCreate.ValueBool() != state.SnatCreate.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.InternalPortAccess.IsNull() != state.InternalPortAccess.IsNull() || plan.InternalPortAccess.IsUnknown() != state.InternalPortAccess.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.InternalPortAccess.IsNull() && !plan.InternalPortAccess.IsUnknown() && !state.InternalPortAccess.IsNull() && !state.InternalPortAccess.IsUnknown() { + if plan.InternalPortAccess.ValueString() != state.InternalPortAccess.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.InternalAddrAccess.IsNull() != state.InternalAddrAccess.IsNull() || plan.InternalAddrAccess.IsUnknown() != state.InternalAddrAccess.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.InternalAddrAccess.IsNull() && !plan.InternalAddrAccess.IsUnknown() && !state.InternalAddrAccess.IsNull() && !state.InternalAddrAccess.IsUnknown() { + if plan.InternalAddrAccess.ValueString() != state.InternalAddrAccess.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 627: resources_core.FormatString(plan.ServiceUid), + 628: resources_core.FormatString(plan.IpSpaceName), + 629: resources_core.FormatBool(plan.DnatCreate), + 630: resources_core.FormatBool(plan.SnatCreate), + 631: resources_core.FormatString(plan.InternalPortAccess), + 632: resources_core.FormatString(plan.InternalAddrAccess), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 25, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, + {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, + {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, + {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, + {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, + {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, + {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, + {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, + {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, + {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, + {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcexternalipResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcexternalipModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcexternalipResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcexternalipResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/26_vapp_resource.go b/generated/prod/go/26_vapp_resource.go new file mode 100644 index 0000000..8d4b291 --- /dev/null +++ b/generated/prod/go/26_vapp_resource.go @@ -0,0 +1,485 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vapp +// Service ID: 26 + +var _ resource.Resource = &VappResource{} +var _ resource.ResourceWithModifyPlan = &VappResource{} +var _ resource.ResourceWithImportState = &VappResource{} + +type VappResource struct { + client *core.UniversalClient +} + +type VappModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + NsxtUid types.String `tfsdk:"nsxt_uid"` + VappName types.String `tfsdk:"vapp_name"` + VdcUid types.String `tfsdk:"vdc_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVappResource() resource.Resource { + return &VappResource{} +} + +func (r *VappResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vapp" +} + +func (r *VappResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "nsxt_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID Услуги `Сетевой шлюз периметра (Edge)` Не изменяется после создания."}, + "vapp_name": schema.StringAttribute{Required: true, MarkdownDescription: "Уникальное в рамках организации Не изменяется после создания."}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VappResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VappModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VappModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VappModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, plan.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != plan.NsxtUid.ValueString() { + plan.NsxtUid = types.StringValue(resolvedNsxtUid) + planChanged = true + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + if plan.NsxtUid.ValueString() != state.NsxtUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { + if plan.VappName.ValueString() != state.VappName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.NsxtUid.IsNull() || config.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if config.VappName.IsNull() || config.VappName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") + return + } + if config.VdcUid.IsNull() || config.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { + config.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + params := map[int]string{ + 190: resources_core.FormatString(config.NsxtUid), + 191: resources_core.FormatString(config.VappName), + 623: resources_core.FormatString(config.VdcUid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 26, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VappResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VappModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if data.VappName.IsNull() || data.VappName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") + return + } + if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { + data.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 190: resources_core.FormatString(data.NsxtUid), + 191: resources_core.FormatString(data.VappName), + 623: resources_core.FormatString(data.VdcUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 26, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VappResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VappModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 26, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { + newState.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VappModel + var state VappModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 26, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "vappName", Field: "VappName", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VappResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VappModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VappResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VappResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/27_vc_vm_v2_resource.go b/generated/prod/go/27_vc_vm_v2_resource.go new file mode 100644 index 0000000..1253004 --- /dev/null +++ b/generated/prod/go/27_vc_vm_v2_resource.go @@ -0,0 +1,332 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vm_v2 +// Service ID: 27 + +var _ resource.Resource = &VcVmV2Resource{} +var _ resource.ResourceWithModifyPlan = &VcVmV2Resource{} +var _ resource.ResourceWithImportState = &VcVmV2Resource{} + +type VcVmV2Resource struct { + client *core.UniversalClient +} + +type VcVmV2Model struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ServerListJson types.String `tfsdk:"server_list_json"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVmV2Resource() resource.Resource { + return &VcVmV2Resource{} +} + +func (r *VcVmV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vm_v2" +} + +func (r *VcVmV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "server_list_json": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVmV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVmV2Model + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVmV2Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVmV2Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 301: resources_core.FormatString(config.ServerListJson), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 27, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVmV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVmV2Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 301: resources_core.FormatString(data.ServerListJson), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 27, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVmV2Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 27, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVmV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVmV2Model + var state VcVmV2Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ServerListJson.IsNull() != state.ServerListJson.IsNull() || plan.ServerListJson.IsUnknown() != state.ServerListJson.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ServerListJson.IsNull() && !plan.ServerListJson.IsUnknown() && !state.ServerListJson.IsNull() && !state.ServerListJson.IsUnknown() { + if plan.ServerListJson.ValueString() != state.ServerListJson.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 302: resources_core.FormatString(plan.ServerListJson), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 27, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVmV2Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVmV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVmV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/28_vc_vm_v3_redeploy_action.go b/generated/prod/go/28_vc_vm_v3_redeploy_action.go new file mode 100644 index 0000000..cd95950 --- /dev/null +++ b/generated/prod/go/28_vc_vm_v3_redeploy_action.go @@ -0,0 +1,201 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vm_v3 +// Action: redeploy + +var _ resource.Resource = &VcVmV3RedeployResource{} + +// VcVmV3RedeployResource triggers a one-time action. +type VcVmV3RedeployResource struct { + client *core.UniversalClient +} + +type VcVmV3RedeployModel struct { + ID types.String `tfsdk:"id"` + VcVmV3ID types.String `tfsdk:"vc_vm_v3_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + VmName types.String `tfsdk:"vm_name"` + VmCpu types.Int64 `tfsdk:"vm_cpu"` + VmRam types.Int64 `tfsdk:"vm_ram"` + VmDisk types.Int64 `tfsdk:"vm_disk"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + AccessIpList types.String `tfsdk:"access_ip_list"` + ImageVm types.String `tfsdk:"image_vm"` + UserLogin types.String `tfsdk:"user_login"` + UserPublicKey types.String `tfsdk:"user_public_key"` + CloudInit types.String `tfsdk:"cloud_init"` + AccessPortList types.String `tfsdk:"access_port_list"` +} + +func NewVcVmV3RedeployResource() resource.Resource { + return &VcVmV3RedeployResource{} +} + +func (r *VcVmV3RedeployResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vm_v3_redeploy" +} + +func (r *VcVmV3RedeployResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "vc_vm_v3_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "vm_name": schema.StringAttribute{Required: true}, + "vm_cpu": schema.Int64Attribute{Required: true, MarkdownDescription: "шт"}, + "vm_ram": schema.Int64Attribute{Required: true, MarkdownDescription: "Gb"}, + "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, + "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev"}, + "access_ip_list": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]"}, + "image_vm": schema.StringAttribute{Required: true}, + "user_login": schema.StringAttribute{Required: true, MarkdownDescription: "Учётка SSH"}, + "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя"}, + "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов."}, + "access_port_list": schema.StringAttribute{Optional: true, MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVmV3RedeployResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan VcVmV3RedeployModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.VmName.IsNull() || plan.VmName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vm_name is required.") + return + } + if plan.VmCpu.IsNull() || plan.VmCpu.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vm_cpu is required.") + return + } + if plan.VmRam.IsNull() || plan.VmRam.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vm_ram is required.") + return + } + if plan.ImageVm.IsNull() || plan.ImageVm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") + return + } + if plan.UserLogin.IsNull() || plan.UserLogin.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "user_login is required.") + return + } + if plan.UserPublicKey.IsNull() || plan.UserPublicKey.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") + return + } + + instanceUID := strings.TrimSpace(plan.VcVmV3ID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "vmName": resources_core.FormatString(plan.VmName), + "vmCpu": resources_core.FormatInt64(plan.VmCpu), + "vmRam": resources_core.FormatInt64(plan.VmRam), + "vmDisk": resources_core.FormatInt64(plan.VmDisk), + "ipSpaceName": resources_core.FormatString(plan.IpSpaceName), + "accessIpList": resources_core.FormatString(plan.AccessIpList), + "imageVm": resources_core.FormatString(plan.ImageVm), + "userLogin": resources_core.FormatString(plan.UserLogin), + "userPublicKey": resources_core.FormatString(plan.UserPublicKey), + "cloudInit": resources_core.FormatString(plan.CloudInit), + "accessPortList": resources_core.FormatString(plan.AccessPortList), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "redeploy", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *VcVmV3RedeployResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVmV3RedeployModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmV3RedeployResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVmV3RedeployModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.VcVmV3ID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "vmName": resources_core.FormatString(plan.VmName), + "vmCpu": resources_core.FormatInt64(plan.VmCpu), + "vmRam": resources_core.FormatInt64(plan.VmRam), + "vmDisk": resources_core.FormatInt64(plan.VmDisk), + "ipSpaceName": resources_core.FormatString(plan.IpSpaceName), + "accessIpList": resources_core.FormatString(plan.AccessIpList), + "imageVm": resources_core.FormatString(plan.ImageVm), + "userLogin": resources_core.FormatString(plan.UserLogin), + "userPublicKey": resources_core.FormatString(plan.UserPublicKey), + "cloudInit": resources_core.FormatString(plan.CloudInit), + "accessPortList": resources_core.FormatString(plan.AccessPortList), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *VcVmV3RedeployResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *VcVmV3RedeployResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/28_vc_vm_v3_resource.go b/generated/prod/go/28_vc_vm_v3_resource.go new file mode 100644 index 0000000..f22a472 --- /dev/null +++ b/generated/prod/go/28_vc_vm_v3_resource.go @@ -0,0 +1,586 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vm_v3 +// Service ID: 28 + +var _ resource.Resource = &VcVmV3Resource{} +var _ resource.ResourceWithModifyPlan = &VcVmV3Resource{} +var _ resource.ResourceWithImportState = &VcVmV3Resource{} + +type VcVmV3Resource struct { + client *core.UniversalClient +} + +type VcVmV3Model struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AccessIpList types.String `tfsdk:"access_ip_list"` + AccessPortList types.String `tfsdk:"access_port_list"` + CloudInit types.String `tfsdk:"cloud_init"` + ImageVm types.String `tfsdk:"image_vm"` + IpSpaceName types.String `tfsdk:"ip_space_name"` + UserLogin types.String `tfsdk:"user_login"` + UserPublicKey types.String `tfsdk:"user_public_key"` + VappUid types.String `tfsdk:"vapp_uid"` + VmCpu types.Int64 `tfsdk:"vm_cpu"` + VmDisk types.Int64 `tfsdk:"vm_disk"` + VmName types.String `tfsdk:"vm_name"` + VmRam types.Int64 `tfsdk:"vm_ram"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVmV3Resource() resource.Resource { + return &VcVmV3Resource{} +} + +func (r *VcVmV3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vm_v3" +} + +func (r *VcVmV3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "access_ip_list": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]"}, + "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, + "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, + "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "ip_space_name": schema.StringAttribute{Required: true}, + "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, + "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, + "vapp_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, + "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, + "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, + "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, + "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVmV3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVmV3Model + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVmV3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVmV3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, plan.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != plan.VappUid.ValueString() { + plan.VappUid = types.StringValue(resolvedVappUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.CloudInit.IsNull() && !plan.CloudInit.IsUnknown() && !state.CloudInit.IsNull() && !state.CloudInit.IsUnknown() { + if plan.CloudInit.ValueString() != state.CloudInit.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить cloud_init", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ImageVm.IsNull() && !plan.ImageVm.IsUnknown() && !state.ImageVm.IsNull() && !state.ImageVm.IsUnknown() { + if plan.ImageVm.ValueString() != state.ImageVm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить image_vm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { + if plan.UserLogin.ValueString() != state.UserLogin.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.UserPublicKey.IsNull() && !plan.UserPublicKey.IsUnknown() && !state.UserPublicKey.IsNull() && !state.UserPublicKey.IsUnknown() { + if plan.UserPublicKey.ValueString() != state.UserPublicKey.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить user_public_key", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + if plan.VappUid.ValueString() != state.VappUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { + if plan.VmName.ValueString() != state.VmName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ImageVm.IsNull() || config.ImageVm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") + return + } + if config.IpSpaceName.IsNull() || config.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if config.UserPublicKey.IsNull() || config.UserPublicKey.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") + return + } + if config.VappUid.IsNull() || config.VappUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { + config.VappUid = types.StringValue(resolvedVappUid) + } + } + params := map[int]string{ + 407: resources_core.FormatString(config.VappUid), + 408: resources_core.FormatString(config.VmName), + 409: resources_core.FormatInt64(config.VmCpu), + 410: resources_core.FormatInt64(config.VmRam), + 411: resources_core.FormatInt64(config.VmDisk), + 412: resources_core.FormatString(config.IpSpaceName), + 413: resources_core.FormatString(config.AccessIpList), + 414: resources_core.FormatString(config.ImageVm), + 415: resources_core.FormatString(config.CloudInit), + 416: resources_core.FormatString(config.UserLogin), + 417: resources_core.FormatString(config.UserPublicKey), + 448: resources_core.FormatString(config.AccessPortList), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 28, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVmV3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVmV3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ImageVm.IsNull() || data.ImageVm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") + return + } + if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") + return + } + if data.UserPublicKey.IsNull() || data.UserPublicKey.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") + return + } + if data.VappUid.IsNull() || data.VappUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") + return + } + if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { + data.VappUid = types.StringValue(resolvedVappUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 407: resources_core.FormatString(data.VappUid), + 408: resources_core.FormatString(data.VmName), + 409: resources_core.FormatInt64(data.VmCpu), + 410: resources_core.FormatInt64(data.VmRam), + 411: resources_core.FormatInt64(data.VmDisk), + 412: resources_core.FormatString(data.IpSpaceName), + 413: resources_core.FormatString(data.AccessIpList), + 414: resources_core.FormatString(data.ImageVm), + 415: resources_core.FormatString(data.CloudInit), + 416: resources_core.FormatString(data.UserLogin), + 417: resources_core.FormatString(data.UserPublicKey), + 448: resources_core.FormatString(data.AccessPortList), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 28, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, + {Code: "cloudInit", Field: "CloudInit", Type: "string"}, + {Code: "imageVm", Field: "ImageVm", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, + {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { + state.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmV3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVmV3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 28, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, + {Code: "cloudInit", Field: "CloudInit", Type: "string"}, + {Code: "imageVm", Field: "ImageVm", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, + {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { + newState.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVmV3Model + var state VcVmV3Model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { + if plan.VmCpu.ValueInt64() != state.VmCpu.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { + if plan.VmRam.ValueInt64() != state.VmRam.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.VmDisk.IsNull() != state.VmDisk.IsNull() || plan.VmDisk.IsUnknown() != state.VmDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.VmDisk.IsNull() && !plan.VmDisk.IsUnknown() && !state.VmDisk.IsNull() && !state.VmDisk.IsUnknown() { + if plan.VmDisk.ValueInt64() != state.VmDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { + if plan.IpSpaceName.ValueString() != state.IpSpaceName.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { + if plan.AccessIpList.ValueString() != state.AccessIpList.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AccessPortList.IsNull() != state.AccessPortList.IsNull() || plan.AccessPortList.IsUnknown() != state.AccessPortList.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AccessPortList.IsNull() && !plan.AccessPortList.IsUnknown() && !state.AccessPortList.IsNull() && !state.AccessPortList.IsUnknown() { + if plan.AccessPortList.ValueString() != state.AccessPortList.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 493: resources_core.FormatInt64(plan.VmCpu), + 494: resources_core.FormatInt64(plan.VmRam), + 495: resources_core.FormatInt64(plan.VmDisk), + 496: resources_core.FormatString(plan.IpSpaceName), + 497: resources_core.FormatString(plan.AccessIpList), + 498: resources_core.FormatString(plan.AccessPortList), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 28, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, + {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, + {Code: "cloudInit", Field: "CloudInit", Type: "string"}, + {Code: "imageVm", Field: "ImageVm", Type: "string"}, + {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, + {Code: "vappUid", Field: "VappUid", Type: "string"}, + {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, + {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, + {Code: "vmName", Field: "VmName", Type: "string"}, + {Code: "vmRam", Field: "VmRam", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { + resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) + } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { + state.VappUid = types.StringValue(resolvedVappUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVmV3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVmV3Model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVmV3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVmV3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/29_vc_vdc_group_add_vdc_action.go b/generated/prod/go/29_vc_vdc_group_add_vdc_action.go similarity index 81% rename from provider/internal/resources_gen/29_vc_vdc_group_add_vdc_action.go rename to generated/prod/go/29_vc_vdc_group_add_vdc_action.go index 0ae19b7..5f1a8d9 100644 --- a/provider/internal/resources_gen/29_vc_vdc_group_add_vdc_action.go +++ b/generated/prod/go/29_vc_vdc_group_add_vdc_action.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -24,11 +25,12 @@ type VcVdcGroupAddVdcResource struct { } type VcVdcGroupAddVdcModel struct { - ID types.String `tfsdk:"id"` - VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` - RunID types.String `tfsdk:"run_id"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - VdcUid types.String `tfsdk:"vdc_uid"` + ID types.String `tfsdk:"id"` + VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + VdcUid types.String `tfsdk:"vdc_uid"` } func NewVcVdcGroupAddVdcResource() resource.Resource { @@ -41,11 +43,12 @@ func (r *VcVdcGroupAddVdcResource) Metadata(ctx context.Context, req resource.Me func (r *VcVdcGroupAddVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true}, - "vc_vdc_group_id": schema.StringAttribute{Required: true}, - "run_id": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core)"}, + "id": schema.StringAttribute{Computed: true}, + "vc_vdc_group_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core)"}, } resp.Schema = schema.Schema{Attributes: attrs} } diff --git a/provider/internal/resources_gen/26_vapp_reconcile_action.go b/generated/prod/go/29_vc_vdc_group_reconcile_action.go similarity index 63% rename from provider/internal/resources_gen/26_vapp_reconcile_action.go rename to generated/prod/go/29_vc_vdc_group_reconcile_action.go index 8c7f2ed..2f08406 100644 --- a/provider/internal/resources_gen/26_vapp_reconcile_action.go +++ b/generated/prod/go/29_vc_vdc_group_reconcile_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vapp +// Service: vc_vdc_group // Action: reconcile -var _ resource.Resource = &VappReconcileResource{} +var _ resource.Resource = &VcVdcGroupReconcileResource{} -// VappReconcileResource triggers a one-time action. -type VappReconcileResource struct { +// VcVdcGroupReconcileResource triggers a one-time action. +type VcVdcGroupReconcileResource struct { client *core.UniversalClient } -type VappReconcileModel struct { +type VcVdcGroupReconcileModel struct { ID types.String `tfsdk:"id"` - VappID types.String `tfsdk:"vapp_id"` + VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewVappReconcileResource() resource.Resource { - return &VappReconcileResource{} +func NewVcVdcGroupReconcileResource() resource.Resource { + return &VcVdcGroupReconcileResource{} } -func (r *VappReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vapp_reconcile" +func (r *VcVdcGroupReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc_group_reconcile" } -func (r *VappReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *VcVdcGroupReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "vapp_id": schema.StringAttribute{Required: true}, + "vc_vdc_group_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *VappReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VappReconcileModel +func (r *VcVdcGroupReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan VcVdcGroupReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.VappID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,9 +71,6 @@ func (r *VappReconcileResource) Create(ctx context.Context, req resource.CreateR if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return @@ -85,8 +80,8 @@ func (r *VappReconcileResource) Create(ctx context.Context, req resource.CreateR resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *VappReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VappReconcileModel +func (r *VcVdcGroupReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcGroupReconcileModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *VappReconcileResource) Read(ctx context.Context, req resource.ReadReque resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *VappReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VappReconcileModel +func (r *VcVdcGroupReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcGroupReconcileModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.VappID.ValueString()) + instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -119,11 +114,11 @@ func (r *VappReconcileResource) Update(ctx context.Context, req resource.UpdateR resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *VappReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *VcVdcGroupReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *VappReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *VcVdcGroupReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/provider/internal/resources_gen/29_vc_vdc_group_remove_vdc_action.go b/generated/prod/go/29_vc_vdc_group_remove_vdc_action.go similarity index 82% rename from provider/internal/resources_gen/29_vc_vdc_group_remove_vdc_action.go rename to generated/prod/go/29_vc_vdc_group_remove_vdc_action.go index 381161d..af5945f 100644 --- a/provider/internal/resources_gen/29_vc_vdc_group_remove_vdc_action.go +++ b/generated/prod/go/29_vc_vdc_group_remove_vdc_action.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -24,11 +25,12 @@ type VcVdcGroupRemoveVdcResource struct { } type VcVdcGroupRemoveVdcModel struct { - ID types.String `tfsdk:"id"` - VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` - RunID types.String `tfsdk:"run_id"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - VdcName types.String `tfsdk:"vdc_name"` + ID types.String `tfsdk:"id"` + VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + VdcName types.String `tfsdk:"vdc_name"` } func NewVcVdcGroupRemoveVdcResource() resource.Resource { @@ -41,11 +43,12 @@ func (r *VcVdcGroupRemoveVdcResource) Metadata(ctx context.Context, req resource func (r *VcVdcGroupRemoveVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true}, - "vc_vdc_group_id": schema.StringAttribute{Required: true}, - "run_id": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "vdc_name": schema.StringAttribute{Required: true}, + "id": schema.StringAttribute{Computed: true}, + "vc_vdc_group_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "vdc_name": schema.StringAttribute{Required: true}, } resp.Schema = schema.Schema{Attributes: attrs} } diff --git a/generated/prod/go/29_vc_vdc_group_resource.go b/generated/prod/go/29_vc_vdc_group_resource.go new file mode 100644 index 0000000..61f0b6e --- /dev/null +++ b/generated/prod/go/29_vc_vdc_group_resource.go @@ -0,0 +1,389 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vc_vdc_group +// Service ID: 29 + +var _ resource.Resource = &VcVdcGroupResource{} +var _ resource.ResourceWithModifyPlan = &VcVdcGroupResource{} +var _ resource.ResourceWithImportState = &VcVdcGroupResource{} + +type VcVdcGroupResource struct { + client *core.UniversalClient +} + +type VcVdcGroupModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + VdcUid types.String `tfsdk:"vdc_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVcVdcGroupResource() resource.Resource { + return &VcVdcGroupResource{} +} + +func (r *VcVdcGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vc_vdc_group" +} + +func (r *VcVdcGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core) Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VcVdcGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VcVdcGroupModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VcVdcGroupModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VcVdcGroupModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.VdcUid.IsNull() || config.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + params := map[int]string{ + 618: resources_core.FormatString(config.VdcUid), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 29, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VcVdcGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VcVdcGroupModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 618: resources_core.FormatString(data.VdcUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 29, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VcVdcGroupModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 29, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VcVdcGroupModel + var state VcVdcGroupModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 29, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VcVdcGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VcVdcGroupModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VcVdcGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VcVdcGroupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/2_template_resource.go b/generated/prod/go/2_template_resource.go new file mode 100644 index 0000000..ce37872 --- /dev/null +++ b/generated/prod/go/2_template_resource.go @@ -0,0 +1,424 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: template +// Service ID: 2 + +var _ resource.Resource = &TemplateResource{} +var _ resource.ResourceWithModifyPlan = &TemplateResource{} +var _ resource.ResourceWithImportState = &TemplateResource{} + +type TemplateResource struct { + client *core.UniversalClient +} + +type TemplateModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewTemplateResource() resource.Resource { + return &TemplateResource{} +} + +func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_template" +} + +func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100)}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200)}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *TemplateResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *TemplateModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *TemplateModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan TemplateModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 202: resources_core.FormatInt64(config.ResourceCPU), + 203: resources_core.FormatInt64(config.ResourceMemory), + 204: resources_core.FormatInt64(config.ResourceDisk), + 205: resources_core.FormatInt64(config.ResourceInstances), + 206: resources_core.FormatString(config.ResourceRealm), + 207: resources_core.FormatString(config.Domain), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 2, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *TemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data TemplateModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 202: resources_core.FormatInt64(data.ResourceCPU), + 203: resources_core.FormatInt64(data.ResourceMemory), + 204: resources_core.FormatInt64(data.ResourceDisk), + 205: resources_core.FormatInt64(data.ResourceInstances), + 206: resources_core.FormatString(data.ResourceRealm), + 207: resources_core.FormatString(data.Domain), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 2, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state TemplateModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 2, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan TemplateModel + var state TemplateModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 208: resources_core.FormatInt64(plan.ResourceCPU), + 209: resources_core.FormatInt64(plan.ResourceMemory), + 210: resources_core.FormatInt64(plan.ResourceDisk), + 211: resources_core.FormatInt64(plan.ResourceInstances), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 2, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state TemplateModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *TemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/32_vmpostgre_resource.go b/generated/prod/go/32_vmpostgre_resource.go new file mode 100644 index 0000000..d3b40f8 --- /dev/null +++ b/generated/prod/go/32_vmpostgre_resource.go @@ -0,0 +1,594 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: vmpostgre +// Service ID: 32 + +var _ resource.Resource = &VmpostgreResource{} +var _ resource.ResourceWithModifyPlan = &VmpostgreResource{} +var _ resource.ResourceWithImportState = &VmpostgreResource{} + +type VmpostgreResource struct { + client *core.UniversalClient +} + +type VmpostgreModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + NsxtUid types.String `tfsdk:"nsxt_uid"` + ResourceRealm types.String `tfsdk:"resource_realm"` + UserDb types.String `tfsdk:"user_db"` + UserLogin types.String `tfsdk:"user_login"` + UserPassword types.String `tfsdk:"user_password"` + VdcUid types.String `tfsdk:"vdc_uid"` + VmUid types.String `tfsdk:"vm_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewVmpostgreResource() resource.Resource { + return &VmpostgreResource{} +} + +func (r *VmpostgreResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vmpostgre" +} + +func (r *VmpostgreResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "nsxt_uid": schema.StringAttribute{Required: true, MarkdownDescription: "InstanceUid nsxt. Nsxt должен быть создан
Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Ресурсная платформа Не изменяется после создания."}, + "user_db": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("servicedb"), MarkdownDescription: "Наименование Базы данных
Не изменяется после создания."}, + "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("serviceuser"), MarkdownDescription: "Пользователь базы данных
Не изменяется после создания."}, + "user_password": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("fj29gm48fm!d374mc9"), MarkdownDescription: "Пароль от доступа к Базе данных
Не изменяется после создания."}, + "vdc_uid": schema.StringAttribute{Required: true, MarkdownDescription: "InstanceUid vdc. Vdc должен быть создан
Не изменяется после создания."}, + "vm_uid": schema.StringAttribute{Required: true, MarkdownDescription: "InstanceUid ВМ. ВМ должна быть создана
Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *VmpostgreResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *VmpostgreModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *VmpostgreModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan VmpostgreModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, plan.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != plan.NsxtUid.ValueString() { + plan.NsxtUid = types.StringValue(resolvedNsxtUid) + planChanged = true + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, plan.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != plan.VdcUid.ValueString() { + plan.VdcUid = types.StringValue(resolvedVdcUid) + planChanged = true + } + } + if !plan.VmUid.IsNull() && !plan.VmUid.IsUnknown() { + resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, plan.VmUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) + } else if resolvedVmUid != "" && resolvedVmUid != plan.VmUid.ValueString() { + plan.VmUid = types.StringValue(resolvedVmUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + if plan.NsxtUid.ValueString() != state.NsxtUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.UserDb.IsNull() && !plan.UserDb.IsUnknown() && !state.UserDb.IsNull() && !state.UserDb.IsUnknown() { + if plan.UserDb.ValueString() != state.UserDb.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить user_db", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { + if plan.UserLogin.ValueString() != state.UserLogin.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.UserPassword.IsNull() && !plan.UserPassword.IsUnknown() && !state.UserPassword.IsNull() && !state.UserPassword.IsUnknown() { + if plan.UserPassword.ValueString() != state.UserPassword.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить user_password", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + if plan.VdcUid.ValueString() != state.VdcUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.VmUid.IsNull() && !plan.VmUid.IsUnknown() && !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { + if plan.VmUid.ValueString() != state.VmUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить vm_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.NsxtUid.IsNull() || config.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.VdcUid.IsNull() || config.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if config.VmUid.IsNull() || config.VmUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vm_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { + config.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { + config.VdcUid = types.StringValue(resolvedVdcUid) + } + } + if !config.VmUid.IsNull() && !config.VmUid.IsUnknown() { + resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, config.VmUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) + } else if resolvedVmUid != "" && resolvedVmUid != config.VmUid.ValueString() { + config.VmUid = types.StringValue(resolvedVmUid) + } + } + params := map[int]string{ + 43: resources_core.FormatString(config.VdcUid), + 44: resources_core.FormatString(config.NsxtUid), + 45: resources_core.FormatString(config.VmUid), + 46: resources_core.FormatString(config.UserDb), + 47: resources_core.FormatString(config.UserLogin), + 48: resources_core.FormatString(config.UserPassword), + 859: resources_core.FormatString(config.ResourceRealm), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 32, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *VmpostgreResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data VmpostgreModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") + return + } + if data.VmUid.IsNull() || data.VmUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "vm_uid is required.") + return + } + if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { + data.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { + data.VdcUid = types.StringValue(resolvedVdcUid) + } + } + if !data.VmUid.IsNull() && !data.VmUid.IsUnknown() { + resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, data.VmUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedVmUid != "" && resolvedVmUid != data.VmUid.ValueString() { + data.VmUid = types.StringValue(resolvedVmUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 43: resources_core.FormatString(data.VdcUid), + 44: resources_core.FormatString(data.NsxtUid), + 45: resources_core.FormatString(data.VmUid), + 46: resources_core.FormatString(data.UserDb), + 47: resources_core.FormatString(data.UserLogin), + 48: resources_core.FormatString(data.UserPassword), + 859: resources_core.FormatString(data.ResourceRealm), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 32, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "userDb", Field: "UserDb", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPassword", Field: "UserPassword", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "vmUid", Field: "VmUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { + resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, state.VmUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) + } else if resolvedVmUid != "" && resolvedVmUid != state.VmUid.ValueString() { + state.VmUid = types.StringValue(resolvedVmUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VmpostgreResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state VmpostgreModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 32, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "userDb", Field: "UserDb", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPassword", Field: "UserPassword", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "vmUid", Field: "VmUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { + newState.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { + newState.VdcUid = types.StringValue(resolvedVdcUid) + } + } + if !newState.VmUid.IsNull() && !newState.VmUid.IsUnknown() { + resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, newState.VmUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) + } else if resolvedVmUid != "" && resolvedVmUid != newState.VmUid.ValueString() { + newState.VmUid = types.StringValue(resolvedVmUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *VmpostgreResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan VmpostgreModel + var state VmpostgreModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 32, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "userDb", Field: "UserDb", Type: "string"}, + {Code: "userLogin", Field: "UserLogin", Type: "string"}, + {Code: "userPassword", Field: "UserPassword", Type: "string"}, + {Code: "vdcUid", Field: "VdcUid", Type: "string"}, + {Code: "vmUid", Field: "VmUid", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { + resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) + } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { + state.NsxtUid = types.StringValue(resolvedNsxtUid) + } + } + if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { + resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) + } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { + state.VdcUid = types.StringValue(resolvedVdcUid) + } + } + if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { + resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, state.VmUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) + } else if resolvedVmUid != "" && resolvedVmUid != state.VmUid.ValueString() { + state.VmUid = types.StringValue(resolvedVmUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *VmpostgreResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state VmpostgreModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *VmpostgreResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *VmpostgreResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/50_nextcloud_resource.go b/generated/prod/go/50_nextcloud_resource.go new file mode 100644 index 0000000..b5938d0 --- /dev/null +++ b/generated/prod/go/50_nextcloud_resource.go @@ -0,0 +1,584 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nextcloud +// Service ID: 50 + +var _ resource.Resource = &NextcloudResource{} +var _ resource.ResourceWithModifyPlan = &NextcloudResource{} +var _ resource.ResourceWithImportState = &NextcloudResource{} + +type NextcloudResource struct { + client *core.UniversalClient +} + +type NextcloudModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AppVersion types.String `tfsdk:"app_version"` + DocumentServer types.String `tfsdk:"document_server"` + Domain types.String `tfsdk:"domain"` + PsqlUid types.String `tfsdk:"psql_uid"` + RedisUid types.String `tfsdk:"redis_uid"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNextcloudResource() resource.Resource { + return &NextcloudResource{} +} + +func (r *NextcloudResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nextcloud" +} + +func (r *NextcloudResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("32.0.1")}, + "document_server": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("collabora"), MarkdownDescription: "Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ Не изменяется после создания."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=90 (mapped to UUID in core) Не изменяется после создания."}, + "redis_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UI display_name for service_id=91 (mapped to UUID in core) Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)
Диск не может быть меньше 2G (!)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов
Не может пока быть больше одного, так как нужны SC под RWMany"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NextcloudResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NextcloudModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NextcloudModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NextcloudModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, plan.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != plan.PsqlUid.ValueString() { + plan.PsqlUid = types.StringValue(resolvedPsqlUid) + planChanged = true + } + } + if !plan.RedisUid.IsNull() && !plan.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, plan.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != plan.RedisUid.ValueString() { + plan.RedisUid = types.StringValue(resolvedRedisUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.DocumentServer.IsNull() && !plan.DocumentServer.IsUnknown() && !state.DocumentServer.IsNull() && !state.DocumentServer.IsUnknown() { + if plan.DocumentServer.ValueString() != state.DocumentServer.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить document_server", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + if plan.PsqlUid.ValueString() != state.PsqlUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RedisUid.IsNull() && !plan.RedisUid.IsUnknown() && !state.RedisUid.IsNull() && !state.RedisUid.IsUnknown() { + if plan.RedisUid.ValueString() != state.RedisUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить redis_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.PsqlUid.IsNull() || config.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + if config.RedisUid.IsNull() || config.RedisUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "redis_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { + config.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !config.RedisUid.IsNull() && !config.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, config.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != config.RedisUid.ValueString() { + config.RedisUid = types.StringValue(resolvedRedisUid) + } + } + params := map[int]string{ + 21: resources_core.FormatString(config.Domain), + 99: resources_core.FormatInt64(config.ResourceCPU), + 100: resources_core.FormatInt64(config.ResourceMemory), + 101: resources_core.FormatInt64(config.ResourceDisk), + 192: resources_core.FormatString(config.DocumentServer), + 470: resources_core.FormatInt64(config.ResourceInstances), + 564: resources_core.FormatString(config.PsqlUid), + 569: resources_core.FormatString(config.AppVersion), + 570: resources_core.FormatString(config.RedisUid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 50, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NextcloudResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NextcloudModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + if data.RedisUid.IsNull() || data.RedisUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "redis_uid is required.") + return + } + if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { + data.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !data.RedisUid.IsNull() && !data.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, data.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedRedisUid != "" && resolvedRedisUid != data.RedisUid.ValueString() { + data.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 21: resources_core.FormatString(data.Domain), + 99: resources_core.FormatInt64(data.ResourceCPU), + 100: resources_core.FormatInt64(data.ResourceMemory), + 101: resources_core.FormatInt64(data.ResourceDisk), + 192: resources_core.FormatString(data.DocumentServer), + 470: resources_core.FormatInt64(data.ResourceInstances), + 564: resources_core.FormatString(data.PsqlUid), + 569: resources_core.FormatString(data.AppVersion), + 570: resources_core.FormatString(data.RedisUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 50, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "documentServer", Field: "DocumentServer", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "redisUid", Field: "RedisUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !state.RedisUid.IsNull() && !state.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, state.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != state.RedisUid.ValueString() { + state.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NextcloudResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NextcloudModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 50, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "documentServer", Field: "DocumentServer", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "redisUid", Field: "RedisUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { + newState.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !newState.RedisUid.IsNull() && !newState.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, newState.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != newState.RedisUid.ValueString() { + newState.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NextcloudModel + var state NextcloudModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 178: resources_core.FormatInt64(plan.ResourceCPU), + 179: resources_core.FormatInt64(plan.ResourceMemory), + 180: resources_core.FormatInt64(plan.ResourceDisk), + 547: resources_core.FormatInt64(plan.ResourceInstances), + 571: resources_core.FormatString(plan.AppVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 50, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "documentServer", Field: "DocumentServer", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "redisUid", Field: "RedisUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + if !state.RedisUid.IsNull() && !state.RedisUid.IsUnknown() { + resolvedRedisUid, err := r.client.ResolveRefSvcParamValue(ctx, 91, state.RedisUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve redis_uid", err.Error()) + } else if resolvedRedisUid != "" && resolvedRedisUid != state.RedisUid.ValueString() { + state.RedisUid = types.StringValue(resolvedRedisUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NextcloudResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NextcloudModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NextcloudResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NextcloudResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/115_mariadb_reconcile_action.go b/generated/prod/go/50_nextcloud_restart_action.go similarity index 67% rename from provider/internal/resources_gen/115_mariadb_reconcile_action.go rename to generated/prod/go/50_nextcloud_restart_action.go index 5829d16..a3c0260 100644 --- a/provider/internal/resources_gen/115_mariadb_reconcile_action.go +++ b/generated/prod/go/50_nextcloud_restart_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Action: reconcile +// Service: nextcloud +// Action: restart -var _ resource.Resource = &MariadbReconcileResource{} +var _ resource.Resource = &NextcloudRestartResource{} -// MariadbReconcileResource triggers a one-time action. -type MariadbReconcileResource struct { +// NextcloudRestartResource triggers a one-time action. +type NextcloudRestartResource struct { client *core.UniversalClient } -type MariadbReconcileModel struct { +type NextcloudRestartModel struct { ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` + NextcloudID types.String `tfsdk:"nextcloud_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewMariadbReconcileResource() resource.Resource { - return &MariadbReconcileResource{} +func NewNextcloudRestartResource() resource.Resource { + return &NextcloudRestartResource{} } -func (r *MariadbReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_reconcile" +func (r *NextcloudRestartResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nextcloud_restart" } -func (r *MariadbReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *NextcloudRestartResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "mariadb_id": schema.StringAttribute{Required: true}, + "nextcloud_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *MariadbReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbReconcileModel +func (r *NextcloudRestartResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan NextcloudRestartModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) + instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,20 +71,17 @@ func (r *MariadbReconcileResource) Create(ctx context.Context, req resource.Crea if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "restart", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *MariadbReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbReconcileModel +func (r *NextcloudRestartResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NextcloudRestartModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *MariadbReconcileResource) Read(ctx context.Context, req resource.ReadRe resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *MariadbReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbReconcileModel +func (r *NextcloudRestartResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NextcloudRestartModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) + instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -110,20 +105,20 @@ func (r *MariadbReconcileResource) Update(ctx context.Context, req resource.Upda params := map[string]string{} - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "restart", params); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *MariadbReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *NextcloudRestartResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *MariadbReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *NextcloudRestartResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/81_superset_resource.go b/generated/prod/go/81_superset_resource.go new file mode 100644 index 0000000..5dcb375 --- /dev/null +++ b/generated/prod/go/81_superset_resource.go @@ -0,0 +1,417 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: superset +// Service ID: 81 + +var _ resource.Resource = &SupersetResource{} +var _ resource.ResourceWithModifyPlan = &SupersetResource{} +var _ resource.ResourceWithImportState = &SupersetResource{} + +type SupersetResource struct { + client *core.UniversalClient +} + +type SupersetModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + Emails types.String `tfsdk:"emails"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewSupersetResource() resource.Resource { + return &SupersetResource{} +} + +func (r *SupersetResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_superset" +} + +func (r *SupersetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *SupersetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *SupersetModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *SupersetModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan SupersetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { + if plan.Emails.ValueString() != state.Emails.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 14: resources_core.FormatString(config.Domain), + 20: resources_core.FormatString(config.Emails), + 96: resources_core.FormatInt64(config.ResourceCPU), + 97: resources_core.FormatInt64(config.ResourceMemory), + 98: resources_core.FormatInt64(config.ResourceDisk), + 109: resources_core.FormatString(config.ResourceRealm), + 227: resources_core.FormatInt64(config.ResourceInstances), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 81, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *SupersetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SupersetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 14: resources_core.FormatString(data.Domain), + 20: resources_core.FormatString(data.Emails), + 96: resources_core.FormatInt64(data.ResourceCPU), + 97: resources_core.FormatInt64(data.ResourceMemory), + 98: resources_core.FormatInt64(data.ResourceDisk), + 109: resources_core.FormatString(data.ResourceRealm), + 227: resources_core.FormatInt64(data.ResourceInstances), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 81, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *SupersetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state SupersetModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 81, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan SupersetModel + var state SupersetModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 81, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *SupersetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state SupersetModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *SupersetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *SupersetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/82_harbor_resource.go b/generated/prod/go/82_harbor_resource.go new file mode 100644 index 0000000..ef19c23 --- /dev/null +++ b/generated/prod/go/82_harbor_resource.go @@ -0,0 +1,446 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: harbor +// Service ID: 82 + +var _ resource.Resource = &HarborResource{} +var _ resource.ResourceWithModifyPlan = &HarborResource{} +var _ resource.ResourceWithImportState = &HarborResource{} + +type HarborResource struct { + client *core.UniversalClient +} + +type HarborModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + Emails types.String `tfsdk:"emails"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewHarborResource() resource.Resource { + return &HarborResource{} +} + +func (r *HarborResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_harbor" +} + +func (r *HarborResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID экземпляра услуги S3 Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *HarborResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *HarborModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *HarborModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan HarborModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { + if plan.Emails.ValueString() != state.Emails.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 16: resources_core.FormatString(config.Domain), + 17: resources_core.FormatString(config.Emails), + 110: resources_core.FormatString(config.ResourceRealm), + 228: resources_core.FormatInt64(config.ResourceCPU), + 229: resources_core.FormatInt64(config.ResourceMemory), + 230: resources_core.FormatInt64(config.ResourceInstances), + 231: resources_core.FormatString(config.S3Uid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 82, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *HarborResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data HarborModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 16: resources_core.FormatString(data.Domain), + 17: resources_core.FormatString(data.Emails), + 110: resources_core.FormatString(data.ResourceRealm), + 228: resources_core.FormatInt64(data.ResourceCPU), + 229: resources_core.FormatInt64(data.ResourceMemory), + 230: resources_core.FormatInt64(data.ResourceInstances), + 231: resources_core.FormatString(data.S3Uid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 82, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HarborResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state HarborModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 82, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan HarborModel + var state HarborModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 82, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "emails", Field: "Emails", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HarborResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state HarborModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *HarborResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *HarborResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/88_k8s_ziti_controller_resource.go b/generated/prod/go/88_k8s_ziti_controller_resource.go new file mode 100644 index 0000000..7ad4d64 --- /dev/null +++ b/generated/prod/go/88_k8s_ziti_controller_resource.go @@ -0,0 +1,404 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: k8s_ziti_controller +// Service ID: 88 + +var _ resource.Resource = &K8sZitiControllerResource{} +var _ resource.ResourceWithModifyPlan = &K8sZitiControllerResource{} +var _ resource.ResourceWithImportState = &K8sZitiControllerResource{} + +type K8sZitiControllerResource struct { + client *core.UniversalClient +} + +type K8sZitiControllerModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewK8sZitiControllerResource() resource.Resource { + return &K8sZitiControllerResource{} +} + +func (r *K8sZitiControllerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_k8s_ziti_controller" +} + +func (r *K8sZitiControllerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores) Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *K8sZitiControllerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *K8sZitiControllerModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *K8sZitiControllerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan K8sZitiControllerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 294: resources_core.FormatInt64(config.ResourceCPU), + 295: resources_core.FormatInt64(config.ResourceMemory), + 296: resources_core.FormatInt64(config.ResourceDisk), + 297: resources_core.FormatInt64(config.ResourceInstances), + 298: resources_core.FormatString(config.ResourceRealm), + 299: resources_core.FormatString(config.Domain), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 88, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *K8sZitiControllerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data K8sZitiControllerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 294: resources_core.FormatInt64(data.ResourceCPU), + 295: resources_core.FormatInt64(data.ResourceMemory), + 296: resources_core.FormatInt64(data.ResourceDisk), + 297: resources_core.FormatInt64(data.ResourceInstances), + 298: resources_core.FormatString(data.ResourceRealm), + 299: resources_core.FormatString(data.Domain), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 88, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sZitiControllerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state K8sZitiControllerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 88, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan K8sZitiControllerModel + var state K8sZitiControllerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 88, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *K8sZitiControllerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state K8sZitiControllerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *K8sZitiControllerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *K8sZitiControllerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/89_flask_resource.go b/generated/prod/go/89_flask_resource.go new file mode 100644 index 0000000..6b11602 --- /dev/null +++ b/generated/prod/go/89_flask_resource.go @@ -0,0 +1,454 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: flask +// Service ID: 89 + +var _ resource.Resource = &FlaskResource{} +var _ resource.ResourceWithModifyPlan = &FlaskResource{} +var _ resource.ResourceWithImportState = &FlaskResource{} + +type FlaskResource struct { + client *core.UniversalClient +} + +type FlaskModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + GitPath types.String `tfsdk:"git_path"` + HealthPath types.String `tfsdk:"health_path"` + JsonEnv types.String `tfsdk:"json_env"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewFlaskResource() resource.Resource { + return &FlaskResource{} +} + +func (r *FlaskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_flask" +} + +func (r *FlaskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/Foxyhhd/Baldurs-Gate-test.git"), MarkdownDescription: "Путь до Git, который будет webroot для сайта"}, + "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код Не изменяется после создания."}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{ "param": "value" }"), MarkdownDescription: "Массив переменных, будут переданы в env приложения"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *FlaskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *FlaskModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *FlaskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan FlaskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.HealthPath.IsNull() && !plan.HealthPath.IsUnknown() && !state.HealthPath.IsNull() && !state.HealthPath.IsUnknown() { + if plan.HealthPath.ValueString() != state.HealthPath.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить health_path", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 248: resources_core.FormatString(config.Domain), + 249: resources_core.FormatString(config.ResourceRealm), + 250: resources_core.FormatInt64(config.ResourceCPU), + 251: resources_core.FormatInt64(config.ResourceMemory), + 252: resources_core.FormatInt64(config.ResourceInstances), + 253: resources_core.FormatString(config.GitPath), + 254: resources_core.FormatString(config.JsonEnv), + 255: resources_core.FormatString(config.HealthPath), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 89, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *FlaskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data FlaskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 248: resources_core.FormatString(data.Domain), + 249: resources_core.FormatString(data.ResourceRealm), + 250: resources_core.FormatInt64(data.ResourceCPU), + 251: resources_core.FormatInt64(data.ResourceMemory), + 252: resources_core.FormatInt64(data.ResourceInstances), + 253: resources_core.FormatString(data.GitPath), + 254: resources_core.FormatString(data.JsonEnv), + 255: resources_core.FormatString(data.HealthPath), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 89, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *FlaskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state FlaskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 89, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan FlaskModel + var state FlaskModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.GitPath.IsNull() != state.GitPath.IsNull() || plan.GitPath.IsUnknown() != state.GitPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.GitPath.IsNull() && !plan.GitPath.IsUnknown() && !state.GitPath.IsNull() && !state.GitPath.IsUnknown() { + if plan.GitPath.ValueString() != state.GitPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 451: resources_core.FormatInt64(plan.ResourceCPU), + 452: resources_core.FormatInt64(plan.ResourceMemory), + 453: resources_core.FormatInt64(plan.ResourceInstances), + 454: resources_core.FormatString(plan.GitPath), + 455: resources_core.FormatString(plan.JsonEnv), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 89, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *FlaskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state FlaskModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *FlaskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *FlaskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/90_postgres_recovery_action.go b/generated/prod/go/90_postgres_recovery_action.go new file mode 100644 index 0000000..edaacb2 --- /dev/null +++ b/generated/prod/go/90_postgres_recovery_action.go @@ -0,0 +1,158 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: postgres +// Action: recovery + +var _ resource.Resource = &PostgresRecoveryResource{} + +// PostgresRecoveryResource triggers a one-time action. +type PostgresRecoveryResource struct { + client *core.UniversalClient +} + +type PostgresRecoveryModel struct { + ID types.String `tfsdk:"id"` + PostgresID types.String `tfsdk:"postgres_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceRealm types.String `tfsdk:"resource_realm"` +} + +func NewPostgresRecoveryResource() resource.Resource { + return &PostgresRecoveryResource{} +} + +func (r *PostgresRecoveryResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_postgres_recovery" +} + +func (r *PostgresRecoveryResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "postgres_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)
"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili)
"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PostgresRecoveryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PostgresRecoveryModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "resourceInstances": resources_core.FormatInt64(plan.ResourceInstances), + "resourceMemory": resources_core.FormatInt64(plan.ResourceMemory), + "resourceCPU": resources_core.FormatInt64(plan.ResourceCPU), + "resourceDisk": resources_core.FormatInt64(plan.ResourceDisk), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "recovery", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "recovery", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PostgresRecoveryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PostgresRecoveryModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PostgresRecoveryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PostgresRecoveryModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "resourceInstances": resources_core.FormatInt64(plan.ResourceInstances), + "resourceMemory": resources_core.FormatInt64(plan.ResourceMemory), + "resourceCPU": resources_core.FormatInt64(plan.ResourceCPU), + "resourceDisk": resources_core.FormatInt64(plan.ResourceDisk), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "recovery", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "recovery", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PostgresRecoveryResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *PostgresRecoveryResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/90_postgres_resource.go b/generated/prod/go/90_postgres_resource.go new file mode 100644 index 0000000..3d54dce --- /dev/null +++ b/generated/prod/go/90_postgres_resource.go @@ -0,0 +1,686 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: postgres +// Service ID: 90 + +var _ resource.Resource = &PostgresResource{} +var _ resource.ResourceWithModifyPlan = &PostgresResource{} +var _ resource.ResourceWithImportState = &PostgresResource{} + +type PostgresResource struct { + client *core.UniversalClient +} + +type PostgresModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AllowNoSSL types.Bool `tfsdk:"allow_no_s_s_l"` + AppVersion types.String `tfsdk:"app_version"` + AutoScale types.Bool `tfsdk:"auto_scale"` + AutoScalePercentage types.Int64 `tfsdk:"auto_scale_percentage"` + AutoScaleQuotaGb types.String `tfsdk:"auto_scale_quota_gb"` + AutoScaleTechWindow types.Int64 `tfsdk:"auto_scale_tech_window"` + EnablePgPoolerMaster types.Bool `tfsdk:"enable_pg_pooler_master"` + EnablePgPoolerSlave types.Bool `tfsdk:"enable_pg_pooler_slave"` + ExtBACKUPNUMTORETAIN types.Int64 `tfsdk:"ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n"` + ExtBACKUPSCHEDULE types.String `tfsdk:"ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + IpSpaceNameSlave types.String `tfsdk:"ip_space_name_slave"` + JsonParameters types.String `tfsdk:"json_parameters"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + NeedExternalAddressSlave types.Bool `tfsdk:"need_external_address_slave"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.String `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + S3Uid types.String `tfsdk:"s3_uid"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewPostgresResource() resource.Resource { + return &PostgresResource{} +} + +func (r *PostgresResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_postgres" +} + +func (r *PostgresResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "allow_no_s_s_l": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр"}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("17"), MarkdownDescription: "Версия PostgreSQL"}, + "auto_scale": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги Не изменяется после создания."}, + "auto_scale_percentage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема Не изменяется после создания."}, + "auto_scale_quota_gb": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Квота для autoScale Не изменяется после создания."}, + "auto_scale_tech_window": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) Не изменяется после создания."}, + "enable_pg_pooler_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на запись). Будет создан новый под"}, + "enable_pg_pooler_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на чтение). Будет создан новый под"}, + "ext__b_a_c_k_u_p__n_u_m__t_o__r_e_t_a_i_n": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7), MarkdownDescription: "Не обязательный. Количество сохраняемых бекапов"}, + "ext__b_a_c_k_u_p__s_c_h_e_d_u_l_e": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *"), MarkdownDescription: "Не обязательный. Время запуска бекапа"}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`"}, + "ip_space_name_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave"}, + "json_parameters": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{ \"log_connections\": \"off\", \"log_disconnections\": \"off\" }"), MarkdownDescription: "Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний (белый) IP для доступа к master"}, + "need_external_address_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores)"}, + "resource_disk": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("10"), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PostgresResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *PostgresModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *PostgresModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan PostgresModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, plan.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != plan.S3Uid.ValueString() { + plan.S3Uid = types.StringValue(resolvedS3Uid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.AutoScale.IsNull() && !plan.AutoScale.IsUnknown() && !state.AutoScale.IsNull() && !state.AutoScale.IsUnknown() { + if plan.AutoScale.ValueBool() != state.AutoScale.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.AutoScalePercentage.IsNull() && !plan.AutoScalePercentage.IsUnknown() && !state.AutoScalePercentage.IsNull() && !state.AutoScalePercentage.IsUnknown() { + if plan.AutoScalePercentage.ValueInt64() != state.AutoScalePercentage.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale_percentage", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.AutoScaleQuotaGb.IsNull() && !plan.AutoScaleQuotaGb.IsUnknown() && !state.AutoScaleQuotaGb.IsNull() && !state.AutoScaleQuotaGb.IsUnknown() { + if plan.AutoScaleQuotaGb.ValueString() != state.AutoScaleQuotaGb.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale_quota_gb", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.AutoScaleTechWindow.IsNull() && !plan.AutoScaleTechWindow.IsUnknown() && !state.AutoScaleTechWindow.IsNull() && !state.AutoScaleTechWindow.IsUnknown() { + if plan.AutoScaleTechWindow.ValueInt64() != state.AutoScaleTechWindow.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить auto_scale_tech_window", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { + if plan.S3Uid.ValueString() != state.S3Uid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.S3Uid.IsNull() || config.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { + resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) + } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { + config.S3Uid = types.StringValue(resolvedS3Uid) + } + } + params := map[int]string{ + 23: resources_core.FormatString(config.S3Uid), + 80: resources_core.FormatInt64(config.ResourceInstances), + 81: resources_core.FormatInt64(config.ResourceMemory), + 82: resources_core.FormatInt64(config.ResourceCPU), + 83: resources_core.FormatString(config.ResourceDisk), + 102: resources_core.FormatString(config.ResourceRealm), + 145: resources_core.FormatBool(config.NeedExternalAddressMaster), + 146: resources_core.FormatBool(config.NeedExternalAddressSlave), + 265: resources_core.FormatString(config.ExtBACKUPSCHEDULE), + 266: resources_core.FormatInt64(config.ExtBACKUPNUMTORETAIN), + 310: resources_core.FormatString(config.AppVersion), + 311: resources_core.FormatString(config.JsonParameters), + 312: resources_core.FormatBool(config.EnablePgPoolerMaster), + 313: resources_core.FormatBool(config.EnablePgPoolerSlave), + 314: resources_core.FormatBool(config.AllowNoSSL), + 329: resources_core.FormatBool(config.AutoScale), + 330: resources_core.FormatInt64(config.AutoScalePercentage), + 331: resources_core.FormatInt64(config.AutoScaleTechWindow), + 337: resources_core.FormatString(config.IpSpaceNameMaster), + 338: resources_core.FormatString(config.IpSpaceNameSlave), + 339: resources_core.FormatString(config.AutoScaleQuotaGb), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 90, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *PostgresResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data PostgresModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 23: resources_core.FormatString(data.S3Uid), + 80: resources_core.FormatInt64(data.ResourceInstances), + 81: resources_core.FormatInt64(data.ResourceMemory), + 82: resources_core.FormatInt64(data.ResourceCPU), + 83: resources_core.FormatString(data.ResourceDisk), + 102: resources_core.FormatString(data.ResourceRealm), + 145: resources_core.FormatBool(data.NeedExternalAddressMaster), + 146: resources_core.FormatBool(data.NeedExternalAddressSlave), + 265: resources_core.FormatString(data.ExtBACKUPSCHEDULE), + 266: resources_core.FormatInt64(data.ExtBACKUPNUMTORETAIN), + 310: resources_core.FormatString(data.AppVersion), + 311: resources_core.FormatString(data.JsonParameters), + 312: resources_core.FormatBool(data.EnablePgPoolerMaster), + 313: resources_core.FormatBool(data.EnablePgPoolerSlave), + 314: resources_core.FormatBool(data.AllowNoSSL), + 329: resources_core.FormatBool(data.AutoScale), + 330: resources_core.FormatInt64(data.AutoScalePercentage), + 331: resources_core.FormatInt64(data.AutoScaleTechWindow), + 337: resources_core.FormatString(data.IpSpaceNameMaster), + 338: resources_core.FormatString(data.IpSpaceNameSlave), + 339: resources_core.FormatString(data.AutoScaleQuotaGb), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 90, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "allowNoSSL", Field: "AllowNoSSL", Type: "bool"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "string"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "enablePgPoolerMaster", Field: "EnablePgPoolerMaster", Type: "bool"}, + {Code: "enablePgPoolerSlave", Field: "EnablePgPoolerSlave", Type: "bool"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "jsonParameters", Field: "JsonParameters", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "string"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PostgresResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PostgresModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 90, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "allowNoSSL", Field: "AllowNoSSL", Type: "bool"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "string"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "enablePgPoolerMaster", Field: "EnablePgPoolerMaster", Type: "bool"}, + {Code: "enablePgPoolerSlave", Field: "EnablePgPoolerSlave", Type: "bool"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "jsonParameters", Field: "JsonParameters", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "string"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PostgresModel + var state PostgresModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueString() != state.ResourceDisk.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressSlave.IsNull() != state.NeedExternalAddressSlave.IsNull() || plan.NeedExternalAddressSlave.IsUnknown() != state.NeedExternalAddressSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressSlave.IsNull() && !plan.NeedExternalAddressSlave.IsUnknown() && !state.NeedExternalAddressSlave.IsNull() && !state.NeedExternalAddressSlave.IsUnknown() { + if plan.NeedExternalAddressSlave.ValueBool() != state.NeedExternalAddressSlave.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPSCHEDULE.IsNull() != state.ExtBACKUPSCHEDULE.IsNull() || plan.ExtBACKUPSCHEDULE.IsUnknown() != state.ExtBACKUPSCHEDULE.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPSCHEDULE.IsNull() && !plan.ExtBACKUPSCHEDULE.IsUnknown() && !state.ExtBACKUPSCHEDULE.IsNull() && !state.ExtBACKUPSCHEDULE.IsUnknown() { + if plan.ExtBACKUPSCHEDULE.ValueString() != state.ExtBACKUPSCHEDULE.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ExtBACKUPNUMTORETAIN.IsNull() != state.ExtBACKUPNUMTORETAIN.IsNull() || plan.ExtBACKUPNUMTORETAIN.IsUnknown() != state.ExtBACKUPNUMTORETAIN.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ExtBACKUPNUMTORETAIN.IsNull() && !plan.ExtBACKUPNUMTORETAIN.IsUnknown() && !state.ExtBACKUPNUMTORETAIN.IsNull() && !state.ExtBACKUPNUMTORETAIN.IsUnknown() { + if plan.ExtBACKUPNUMTORETAIN.ValueInt64() != state.ExtBACKUPNUMTORETAIN.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonParameters.IsNull() != state.JsonParameters.IsNull() || plan.JsonParameters.IsUnknown() != state.JsonParameters.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonParameters.IsNull() && !plan.JsonParameters.IsUnknown() && !state.JsonParameters.IsNull() && !state.JsonParameters.IsUnknown() { + if plan.JsonParameters.ValueString() != state.JsonParameters.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.EnablePgPoolerMaster.IsNull() != state.EnablePgPoolerMaster.IsNull() || plan.EnablePgPoolerMaster.IsUnknown() != state.EnablePgPoolerMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.EnablePgPoolerMaster.IsNull() && !plan.EnablePgPoolerMaster.IsUnknown() && !state.EnablePgPoolerMaster.IsNull() && !state.EnablePgPoolerMaster.IsUnknown() { + if plan.EnablePgPoolerMaster.ValueBool() != state.EnablePgPoolerMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.EnablePgPoolerSlave.IsNull() != state.EnablePgPoolerSlave.IsNull() || plan.EnablePgPoolerSlave.IsUnknown() != state.EnablePgPoolerSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.EnablePgPoolerSlave.IsNull() && !plan.EnablePgPoolerSlave.IsUnknown() && !state.EnablePgPoolerSlave.IsNull() && !state.EnablePgPoolerSlave.IsUnknown() { + if plan.EnablePgPoolerSlave.ValueBool() != state.EnablePgPoolerSlave.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AllowNoSSL.IsNull() != state.AllowNoSSL.IsNull() || plan.AllowNoSSL.IsUnknown() != state.AllowNoSSL.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AllowNoSSL.IsNull() && !plan.AllowNoSSL.IsUnknown() && !state.AllowNoSSL.IsNull() && !state.AllowNoSSL.IsUnknown() { + if plan.AllowNoSSL.ValueBool() != state.AllowNoSSL.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameSlave.IsNull() != state.IpSpaceNameSlave.IsNull() || plan.IpSpaceNameSlave.IsUnknown() != state.IpSpaceNameSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameSlave.IsNull() && !plan.IpSpaceNameSlave.IsUnknown() && !state.IpSpaceNameSlave.IsNull() && !state.IpSpaceNameSlave.IsUnknown() { + if plan.IpSpaceNameSlave.ValueString() != state.IpSpaceNameSlave.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 91: resources_core.FormatInt64(plan.ResourceInstances), + 92: resources_core.FormatInt64(plan.ResourceMemory), + 93: resources_core.FormatInt64(plan.ResourceCPU), + 94: resources_core.FormatString(plan.ResourceDisk), + 147: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 148: resources_core.FormatBool(plan.NeedExternalAddressSlave), + 267: resources_core.FormatString(plan.ExtBACKUPSCHEDULE), + 268: resources_core.FormatInt64(plan.ExtBACKUPNUMTORETAIN), + 315: resources_core.FormatString(plan.AppVersion), + 316: resources_core.FormatString(plan.JsonParameters), + 317: resources_core.FormatBool(plan.EnablePgPoolerMaster), + 318: resources_core.FormatBool(plan.EnablePgPoolerSlave), + 319: resources_core.FormatBool(plan.AllowNoSSL), + 541: resources_core.FormatString(plan.IpSpaceNameMaster), + 542: resources_core.FormatString(plan.IpSpaceNameSlave), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 90, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "allowNoSSL", Field: "AllowNoSSL", Type: "bool"}, + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "autoScale", Field: "AutoScale", Type: "bool"}, + {Code: "autoScalePercentage", Field: "AutoScalePercentage", Type: "int64"}, + {Code: "autoScaleQuotaGb", Field: "AutoScaleQuotaGb", Type: "string"}, + {Code: "autoScaleTechWindow", Field: "AutoScaleTechWindow", Type: "int64"}, + {Code: "enablePgPoolerMaster", Field: "EnablePgPoolerMaster", Type: "bool"}, + {Code: "enablePgPoolerSlave", Field: "EnablePgPoolerSlave", Type: "bool"}, + {Code: "ext_BACKUP_NUM_TO_RETAIN", Field: "ExtBACKUPNUMTORETAIN", Type: "int64"}, + {Code: "ext_BACKUP_SCHEDULE", Field: "ExtBACKUPSCHEDULE", Type: "string"}, + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "jsonParameters", Field: "JsonParameters", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "string"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PostgresResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PostgresModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *PostgresResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *PostgresResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/28_vc_vm_v3_reconcile_action.go b/generated/prod/go/90_postgres_restart_action.go similarity index 67% rename from provider/internal/resources_gen/28_vc_vm_v3_reconcile_action.go rename to generated/prod/go/90_postgres_restart_action.go index 7fbdc54..4d8e202 100644 --- a/provider/internal/resources_gen/28_vc_vm_v3_reconcile_action.go +++ b/generated/prod/go/90_postgres_restart_action.go @@ -14,53 +14,51 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v3 -// Action: reconcile +// Service: postgres +// Action: restart -var _ resource.Resource = &VcVmV3ReconcileResource{} +var _ resource.Resource = &PostgresRestartResource{} -// VcVmV3ReconcileResource triggers a one-time action. -type VcVmV3ReconcileResource struct { +// PostgresRestartResource triggers a one-time action. +type PostgresRestartResource struct { client *core.UniversalClient } -type VcVmV3ReconcileModel struct { +type PostgresRestartModel struct { ID types.String `tfsdk:"id"` - VcVmV3ID types.String `tfsdk:"vc_vm_v3_id"` + PostgresID types.String `tfsdk:"postgres_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` } -func NewVcVmV3ReconcileResource() resource.Resource { - return &VcVmV3ReconcileResource{} +func NewPostgresRestartResource() resource.Resource { + return &PostgresRestartResource{} } -func (r *VcVmV3ReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v3_reconcile" +func (r *PostgresRestartResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_postgres_restart" } -func (r *VcVmV3ReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *PostgresRestartResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "vc_vm_v3_id": schema.StringAttribute{Required: true}, + "postgres_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *VcVmV3ReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcVmV3ReconcileModel +func (r *PostgresRestartResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PostgresRestartModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.VcVmV3ID.ValueString()) + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -73,20 +71,17 @@ func (r *VcVmV3ReconcileResource) Create(ctx context.Context, req resource.Creat if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "restart", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *VcVmV3ReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV3ReconcileModel +func (r *PostgresRestartResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PostgresRestartModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,14 +89,14 @@ func (r *VcVmV3ReconcileResource) Read(ctx context.Context, req resource.ReadReq resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *VcVmV3ReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV3ReconcileModel +func (r *PostgresRestartResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PostgresRestartModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.VcVmV3ID.ValueString()) + instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -110,20 +105,20 @@ func (r *VcVmV3ReconcileResource) Update(ctx context.Context, req resource.Updat params := map[string]string{} - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "restart", params); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *VcVmV3ReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *PostgresRestartResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *VcVmV3ReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *PostgresRestartResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/91_redis_resource.go b/generated/prod/go/91_redis_resource.go new file mode 100644 index 0000000..bcb9db7 --- /dev/null +++ b/generated/prod/go/91_redis_resource.go @@ -0,0 +1,436 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: redis +// Service ID: 91 + +var _ resource.Resource = &RedisResource{} +var _ resource.ResourceWithModifyPlan = &RedisResource{} +var _ resource.ResourceWithImportState = &RedisResource{} + +type RedisResource struct { + client *core.UniversalClient +} + +type RedisModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + IpSpaceNameSlave types.String `tfsdk:"ip_space_name_slave"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + NeedExternalAddressSlave types.Bool `tfsdk:"need_external_address_slave"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewRedisResource() resource.Resource { + return &RedisResource{} +} + +func (r *RedisResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_redis" +} + +func (r *RedisResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster Не изменяется после создания."}, + "ip_space_name_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave Не изменяется после создания."}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний IP для доступа к master Не изменяется после создания."}, + "need_external_address_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Квота ядра пода (Указывается в mili) Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *RedisResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *RedisModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *RedisModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan RedisModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить ip_space_name_master", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.IpSpaceNameSlave.IsNull() && !plan.IpSpaceNameSlave.IsUnknown() && !state.IpSpaceNameSlave.IsNull() && !state.IpSpaceNameSlave.IsUnknown() { + if plan.IpSpaceNameSlave.ValueString() != state.IpSpaceNameSlave.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить ip_space_name_slave", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_master", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressSlave.IsNull() && !plan.NeedExternalAddressSlave.IsUnknown() && !state.NeedExternalAddressSlave.IsNull() && !state.NeedExternalAddressSlave.IsUnknown() { + if plan.NeedExternalAddressSlave.ValueBool() != state.NeedExternalAddressSlave.ValueBool() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_slave", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 112: resources_core.FormatInt64(config.ResourceCPU), + 113: resources_core.FormatInt64(config.ResourceMemory), + 114: resources_core.FormatInt64(config.ResourceDisk), + 181: resources_core.FormatInt64(config.ResourceInstances), + 182: resources_core.FormatString(config.ResourceRealm), + 183: resources_core.FormatBool(config.NeedExternalAddressMaster), + 184: resources_core.FormatBool(config.NeedExternalAddressSlave), + 548: resources_core.FormatString(config.IpSpaceNameMaster), + 549: resources_core.FormatString(config.IpSpaceNameSlave), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 91, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *RedisResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data RedisModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 112: resources_core.FormatInt64(data.ResourceCPU), + 113: resources_core.FormatInt64(data.ResourceMemory), + 114: resources_core.FormatInt64(data.ResourceDisk), + 181: resources_core.FormatInt64(data.ResourceInstances), + 182: resources_core.FormatString(data.ResourceRealm), + 183: resources_core.FormatBool(data.NeedExternalAddressMaster), + 184: resources_core.FormatBool(data.NeedExternalAddressSlave), + 548: resources_core.FormatString(data.IpSpaceNameMaster), + 549: resources_core.FormatString(data.IpSpaceNameSlave), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 91, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RedisResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state RedisModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 91, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan RedisModel + var state RedisModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 91, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RedisResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state RedisModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *RedisResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *RedisResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/92_mongodb_resource.go b/generated/prod/go/92_mongodb_resource.go new file mode 100644 index 0000000..ef456bf --- /dev/null +++ b/generated/prod/go/92_mongodb_resource.go @@ -0,0 +1,445 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mongodb +// Service ID: 92 + +var _ resource.Resource = &MongodbResource{} +var _ resource.ResourceWithModifyPlan = &MongodbResource{} +var _ resource.ResourceWithImportState = &MongodbResource{} + +type MongodbResource struct { + client *core.UniversalClient +} + +type MongodbModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + NeedExternalAddressMaster types.String `tfsdk:"need_external_address_master"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewMongodbResource() resource.Resource { + return &MongodbResource{} +} + +func (r *MongodbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mongodb" +} + +func (r *MongodbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1` Не изменяется после создания."}, + "need_external_address_master": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("false"), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false` Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `1000` Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `20` Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2` Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2048` Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MongodbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *MongodbModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *MongodbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan MongodbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить ip_space_name_master", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueString() != state.NeedExternalAddressMaster.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить need_external_address_master", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceCPU.IsNull() || config.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if config.ResourceDisk.IsNull() || config.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if config.ResourceInstances.IsNull() || config.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if config.ResourceMemory.IsNull() || config.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 487: resources_core.FormatInt64(config.ResourceInstances), + 588: resources_core.FormatInt64(config.ResourceMemory), + 589: resources_core.FormatInt64(config.ResourceCPU), + 590: resources_core.FormatInt64(config.ResourceDisk), + 595: resources_core.FormatString(config.ResourceRealm), + 645: resources_core.FormatString(config.NeedExternalAddressMaster), + 646: resources_core.FormatString(config.IpSpaceNameMaster), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 92, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *MongodbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data MongodbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceCPU.IsNull() || data.ResourceCPU.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") + return + } + if data.ResourceDisk.IsNull() || data.ResourceDisk.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") + return + } + if data.ResourceInstances.IsNull() || data.ResourceInstances.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") + return + } + if data.ResourceMemory.IsNull() || data.ResourceMemory.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 487: resources_core.FormatInt64(data.ResourceInstances), + 588: resources_core.FormatInt64(data.ResourceMemory), + 589: resources_core.FormatInt64(data.ResourceCPU), + 590: resources_core.FormatInt64(data.ResourceDisk), + 595: resources_core.FormatString(data.ResourceRealm), + 645: resources_core.FormatString(data.NeedExternalAddressMaster), + 646: resources_core.FormatString(data.IpSpaceNameMaster), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 92, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MongodbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MongodbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 92, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MongodbModel + var state MongodbModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 92, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MongodbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MongodbModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *MongodbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *MongodbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/92_mongodb_user_resource.go b/generated/prod/go/92_mongodb_user_resource.go new file mode 100644 index 0000000..5a450c4 --- /dev/null +++ b/generated/prod/go/92_mongodb_user_resource.go @@ -0,0 +1,357 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: mongodb +// Subresource: user + +var _ resource.Resource = &MongodbUserResource{} + +// MongodbUserResource manages a subresource via operations. +type MongodbUserResource struct { + client *core.UniversalClient +} + +type MongodbUserModel struct { + ID types.String `tfsdk:"id"` + MongodbID types.String `tfsdk:"mongodb_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + DbName types.String `tfsdk:"db_name"` + Role types.String `tfsdk:"role"` + Username types.String `tfsdk:"username"` +} + +func NewMongodbUserResource() resource.Resource { + return &MongodbUserResource{} +} + +func (r *MongodbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_mongodb_user" +} + +func (r *MongodbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "mongodb_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "db_name": schema.StringAttribute{Required: true}, + "role": schema.StringAttribute{Required: true}, + "username": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *MongodbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan MongodbUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.DbName.IsNull() || plan.DbName.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") + return + } + if plan.Role.IsNull() || plan.Role.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "role is required.") + return + } + if plan.Username.IsNull() || plan.Username.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "username is required.") + return + } + + instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + if adoptExistingOnCreate { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "dbName": resources_core.FormatString(plan.DbName), + "role": resources_core.FormatString(plan.Role), + }) + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { + createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) + } + if createErr != nil { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект подтверждён в state_out, выполняется усыновление") + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + } + resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") + return + } + resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) + return + } + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) + return + } + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MongodbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state MongodbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MongodbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan MongodbUserModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + if "modify_user" == "" { + var state MongodbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ID.IsNull() || plan.ID.IsUnknown() { + plan.ID = state.ID + } + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) + if instanceUID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") + return + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(plan.Username), + "dbName": resources_core.FormatString(plan.DbName), + "role": resources_core.FormatString(plan.Role), + }) + + idParams := map[string]string{ + "username": resources_core.FormatString(plan.Username), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MongodbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MongodbUserModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if "delete_user" == "" { + return + } + + instanceUID := strings.TrimSpace(state.MongodbID.ValueString()) + if instanceUID == "" { + return + } + skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() + + idParams := map[string]string{ + "username": resources_core.FormatString(state.Username), + } + identityCodes := []string{ + "username", + } + listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) + targetValue := strings.TrimSpace(idParams[idKey]) + if targetValue == "" { + for _, code := range identityCodes { + candidate := strings.TrimSpace(idParams[code]) + if candidate == "" { + continue + } + targetValue = candidate + idKey = code + break + } + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && !found { + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Объект отсутствует и будет пропущен: "+targetValue) + return + } + resp.Diagnostics.AddError("Подресурс не найден", "Объект отсутствует: "+targetValue) + return + } + } + + params := resources_core.CompactParams(map[string]string{ + "username": resources_core.FormatString(state.Username), + }) + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { + if resources_core.IsSubresourceMissingError(err) { + if targetValue != "" { + found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if checkErr != nil { + resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } + if skipMissingOnDelete { + resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") + return + } + } + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + if targetValue != "" { + found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if known && found { + resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) + return + } + } +} + +func (r *MongodbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/93_rabbitmq_resource.go b/generated/prod/go/93_rabbitmq_resource.go new file mode 100644 index 0000000..7bff3d8 --- /dev/null +++ b/generated/prod/go/93_rabbitmq_resource.go @@ -0,0 +1,468 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: rabbitmq +// Service ID: 93 + +var _ resource.Resource = &RabbitmqResource{} +var _ resource.ResourceWithModifyPlan = &RabbitmqResource{} +var _ resource.ResourceWithImportState = &RabbitmqResource{} + +type RabbitmqResource struct { + client *core.UniversalClient +} + +type RabbitmqModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` + IpSpaceNameSlave types.String `tfsdk:"ip_space_name_slave"` + NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` + NeedExternalAddressSlave types.Bool `tfsdk:"need_external_address_slave"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewRabbitmqResource() resource.Resource { + return &RabbitmqResource{} +} + +func (r *RabbitmqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_rabbitmq" +} + +func (r *RabbitmqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, + "ip_space_name_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, + "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`"}, + "need_external_address_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Выделяет внешний (белый) IP для доступа к slave"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `2000`"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Пример: `20`"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1124), MarkdownDescription: "Пример: `2048`"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *RabbitmqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *RabbitmqModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *RabbitmqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan RabbitmqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 116: resources_core.FormatInt64(config.ResourceCPU), + 117: resources_core.FormatInt64(config.ResourceMemory), + 118: resources_core.FormatInt64(config.ResourceDisk), + 119: resources_core.FormatString(config.ResourceRealm), + 224: resources_core.FormatInt64(config.ResourceInstances), + 225: resources_core.FormatBool(config.NeedExternalAddressMaster), + 543: resources_core.FormatString(config.IpSpaceNameMaster), + } + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 93, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *RabbitmqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data RabbitmqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 116: resources_core.FormatInt64(data.ResourceCPU), + 117: resources_core.FormatInt64(data.ResourceMemory), + 118: resources_core.FormatInt64(data.ResourceDisk), + 119: resources_core.FormatString(data.ResourceRealm), + 224: resources_core.FormatInt64(data.ResourceInstances), + 225: resources_core.FormatBool(data.NeedExternalAddressMaster), + 543: resources_core.FormatString(data.IpSpaceNameMaster), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 93, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RabbitmqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state RabbitmqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 93, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan RabbitmqModel + var state RabbitmqModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { + if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.NeedExternalAddressSlave.IsNull() != state.NeedExternalAddressSlave.IsNull() || plan.NeedExternalAddressSlave.IsUnknown() != state.NeedExternalAddressSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.NeedExternalAddressSlave.IsNull() && !plan.NeedExternalAddressSlave.IsUnknown() && !state.NeedExternalAddressSlave.IsNull() && !state.NeedExternalAddressSlave.IsUnknown() { + if plan.NeedExternalAddressSlave.ValueBool() != state.NeedExternalAddressSlave.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { + if plan.IpSpaceNameMaster.ValueString() != state.IpSpaceNameMaster.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.IpSpaceNameSlave.IsNull() != state.IpSpaceNameSlave.IsNull() || plan.IpSpaceNameSlave.IsUnknown() != state.IpSpaceNameSlave.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.IpSpaceNameSlave.IsNull() && !plan.IpSpaceNameSlave.IsUnknown() && !state.IpSpaceNameSlave.IsNull() && !state.IpSpaceNameSlave.IsUnknown() { + if plan.IpSpaceNameSlave.ValueString() != state.IpSpaceNameSlave.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 304: resources_core.FormatInt64(plan.ResourceCPU), + 305: resources_core.FormatInt64(plan.ResourceMemory), + 306: resources_core.FormatInt64(plan.ResourceDisk), + 307: resources_core.FormatInt64(plan.ResourceInstances), + 308: resources_core.FormatBool(plan.NeedExternalAddressMaster), + 309: resources_core.FormatBool(plan.NeedExternalAddressSlave), + 545: resources_core.FormatString(plan.IpSpaceNameMaster), + 546: resources_core.FormatString(plan.IpSpaceNameSlave), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 93, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, + {Code: "ipSpaceNameSlave", Field: "IpSpaceNameSlave", Type: "string"}, + {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, + {Code: "needExternalAddressSlave", Field: "NeedExternalAddressSlave", Type: "bool"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *RabbitmqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state RabbitmqModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *RabbitmqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *RabbitmqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/89_flask_redeploy_action.go b/generated/prod/go/94_lucee_redeploy_action.go similarity index 65% rename from provider/internal/resources_gen/89_flask_redeploy_action.go rename to generated/prod/go/94_lucee_redeploy_action.go index 2d63c9f..8709266 100644 --- a/provider/internal/resources_gen/89_flask_redeploy_action.go +++ b/generated/prod/go/94_lucee_redeploy_action.go @@ -14,59 +14,57 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: flask +// Service: lucee // Action: redeploy -var _ resource.Resource = &FlaskRedeployResource{} +var _ resource.Resource = &LuceeRedeployResource{} -// FlaskRedeployResource triggers a one-time action. -type FlaskRedeployResource struct { +// LuceeRedeployResource triggers a one-time action. +type LuceeRedeployResource struct { client *core.UniversalClient } -type FlaskRedeployModel struct { +type LuceeRedeployModel struct { ID types.String `tfsdk:"id"` - FlaskID types.String `tfsdk:"flask_id"` + LuceeID types.String `tfsdk:"lucee_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` + ResourceRealm types.String `tfsdk:"resource_realm"` } -func NewFlaskRedeployResource() resource.Resource { - return &FlaskRedeployResource{} +func NewLuceeRedeployResource() resource.Resource { + return &LuceeRedeployResource{} } -func (r *FlaskRedeployResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_flask_redeploy" +func (r *LuceeRedeployResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_lucee_redeploy" } -func (r *FlaskRedeployResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *LuceeRedeployResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "flask_id": schema.StringAttribute{Required: true}, + "lucee_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *FlaskRedeployResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan FlaskRedeployModel +func (r *LuceeRedeployResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan LuceeRedeployModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") return } - instanceUID := strings.TrimSpace(plan.FlaskID.ValueString()) + instanceUID := strings.TrimSpace(plan.LuceeID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -74,16 +72,13 @@ func (r *FlaskRedeployResource) Create(ctx context.Context, req resource.CreateR } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "redeploy", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return @@ -93,8 +88,8 @@ func (r *FlaskRedeployResource) Create(ctx context.Context, req resource.CreateR resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *FlaskRedeployResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state FlaskRedeployModel +func (r *LuceeRedeployResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state LuceeRedeployModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -102,14 +97,14 @@ func (r *FlaskRedeployResource) Read(ctx context.Context, req resource.ReadReque resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *FlaskRedeployResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan FlaskRedeployModel +func (r *LuceeRedeployResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan LuceeRedeployModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.FlaskID.ValueString()) + instanceUID := strings.TrimSpace(plan.LuceeID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") @@ -117,7 +112,7 @@ func (r *FlaskRedeployResource) Update(ctx context.Context, req resource.UpdateR } params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), } if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { @@ -129,11 +124,11 @@ func (r *FlaskRedeployResource) Update(ctx context.Context, req resource.UpdateR resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *FlaskRedeployResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *LuceeRedeployResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *FlaskRedeployResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *LuceeRedeployResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/94_lucee_resource.go b/generated/prod/go/94_lucee_resource.go new file mode 100644 index 0000000..24770c5 --- /dev/null +++ b/generated/prod/go/94_lucee_resource.go @@ -0,0 +1,475 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: lucee +// Service ID: 94 + +var _ resource.Resource = &LuceeResource{} +var _ resource.ResourceWithModifyPlan = &LuceeResource{} +var _ resource.ResourceWithImportState = &LuceeResource{} + +type LuceeResource struct { + client *core.UniversalClient +} + +type LuceeModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AppVersion types.String `tfsdk:"app_version"` + Domain types.String `tfsdk:"domain"` + GitPath types.String `tfsdk:"git_path"` + HealthPath types.String `tfsdk:"health_path"` + JsonEnv types.String `tfsdk:"json_env"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewLuceeResource() resource.Resource { + return &LuceeResource{} +} + +func (r *LuceeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_lucee" +} + +func (r *LuceeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5.4"), MarkdownDescription: "Версия Lucee"}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до Git, который будет webroot для сайта"}, + "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код"}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{ \"param\": \"value\" }"), MarkdownDescription: "Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *LuceeResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *LuceeModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *LuceeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan LuceeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 103: resources_core.FormatString(config.Domain), + 104: resources_core.FormatString(config.GitPath), + 105: resources_core.FormatString(config.JsonEnv), + 106: resources_core.FormatInt64(config.ResourceCPU), + 107: resources_core.FormatInt64(config.ResourceMemory), + 108: resources_core.FormatString(config.ResourceRealm), + 132: resources_core.FormatString(config.HealthPath), + 139: resources_core.FormatInt64(config.ResourceInstances), + 263: resources_core.FormatString(config.AppVersion), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 94, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *LuceeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data LuceeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 103: resources_core.FormatString(data.Domain), + 104: resources_core.FormatString(data.GitPath), + 105: resources_core.FormatString(data.JsonEnv), + 106: resources_core.FormatInt64(data.ResourceCPU), + 107: resources_core.FormatInt64(data.ResourceMemory), + 108: resources_core.FormatString(data.ResourceRealm), + 132: resources_core.FormatString(data.HealthPath), + 139: resources_core.FormatInt64(data.ResourceInstances), + 263: resources_core.FormatString(data.AppVersion), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 94, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *LuceeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state LuceeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 94, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan LuceeModel + var state LuceeModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.GitPath.IsNull() != state.GitPath.IsNull() || plan.GitPath.IsUnknown() != state.GitPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.GitPath.IsNull() && !plan.GitPath.IsUnknown() && !state.GitPath.IsNull() && !state.GitPath.IsUnknown() { + if plan.GitPath.ValueString() != state.GitPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.HealthPath.IsNull() != state.HealthPath.IsNull() || plan.HealthPath.IsUnknown() != state.HealthPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.HealthPath.IsNull() && !plan.HealthPath.IsUnknown() && !state.HealthPath.IsNull() && !state.HealthPath.IsUnknown() { + if plan.HealthPath.ValueString() != state.HealthPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 133: resources_core.FormatString(plan.GitPath), + 134: resources_core.FormatString(plan.HealthPath), + 135: resources_core.FormatString(plan.JsonEnv), + 136: resources_core.FormatInt64(plan.ResourceCPU), + 137: resources_core.FormatInt64(plan.ResourceMemory), + 138: resources_core.FormatInt64(plan.ResourceInstances), + 264: resources_core.FormatString(plan.AppVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 94, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *LuceeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state LuceeModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *LuceeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *LuceeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/94_lucee_restart_action.go b/generated/prod/go/94_lucee_restart_action.go new file mode 100644 index 0000000..9df1530 --- /dev/null +++ b/generated/prod/go/94_lucee_restart_action.go @@ -0,0 +1,131 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: lucee +// Action: restart + +var _ resource.Resource = &LuceeRestartResource{} + +// LuceeRestartResource triggers a one-time action. +type LuceeRestartResource struct { + client *core.UniversalClient +} + +type LuceeRestartModel struct { + ID types.String `tfsdk:"id"` + LuceeID types.String `tfsdk:"lucee_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` +} + +func NewLuceeRestartResource() resource.Resource { + return &LuceeRestartResource{} +} + +func (r *LuceeRestartResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_lucee_restart" +} + +func (r *LuceeRestartResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "lucee_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *LuceeRestartResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan LuceeRestartModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.LuceeID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "restart", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *LuceeRestartResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state LuceeRestartModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *LuceeRestartResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan LuceeRestartModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.LuceeID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{} + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "restart", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "restart", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *LuceeRestartResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *LuceeRestartResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/91_redis_reconcile_action.go b/generated/prod/go/95_nodejs_redeploy_action.go similarity index 64% rename from provider/internal/resources_gen/91_redis_reconcile_action.go rename to generated/prod/go/95_nodejs_redeploy_action.go index 1a113ef..6e63ee5 100644 --- a/provider/internal/resources_gen/91_redis_reconcile_action.go +++ b/generated/prod/go/95_nodejs_redeploy_action.go @@ -14,79 +14,82 @@ import ( ) // Code generated by tools/gen_v2. DO NOT EDIT. -// Service: redis -// Action: reconcile +// Service: nodejs +// Action: redeploy -var _ resource.Resource = &RedisReconcileResource{} +var _ resource.Resource = &NodejsRedeployResource{} -// RedisReconcileResource triggers a one-time action. -type RedisReconcileResource struct { +// NodejsRedeployResource triggers a one-time action. +type NodejsRedeployResource struct { client *core.UniversalClient } -type RedisReconcileModel struct { +type NodejsRedeployModel struct { ID types.String `tfsdk:"id"` - RedisID types.String `tfsdk:"redis_id"` + NodejsID types.String `tfsdk:"nodejs_id"` RunID types.String `tfsdk:"run_id"` AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` + ResourceRealm types.String `tfsdk:"resource_realm"` } -func NewRedisReconcileResource() resource.Resource { - return &RedisReconcileResource{} +func NewNodejsRedeployResource() resource.Resource { + return &NodejsRedeployResource{} } -func (r *RedisReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_redis_reconcile" +func (r *NodejsRedeployResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nodejs_redeploy" } -func (r *RedisReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *NodejsRedeployResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true}, - "redis_id": schema.StringAttribute{Required: true}, + "nodejs_id": schema.StringAttribute{Required: true}, "run_id": schema.StringAttribute{Required: true}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, } resp.Schema = schema.Schema{Attributes: attrs} } -func (r *RedisReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan RedisReconcileModel +func (r *NodejsRedeployResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan NodejsRedeployModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } - instanceUID := strings.TrimSpace(plan.RedisID.ValueString()) + instanceUID := strings.TrimSpace(plan.NodejsID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") return } - params := map[string]string{} + params := map[string]string{ + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "redeploy", params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *RedisReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RedisReconcileModel +func (r *NodejsRedeployResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NodejsRedeployModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -94,36 +97,38 @@ func (r *RedisReconcileResource) Read(ctx context.Context, req resource.ReadRequ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } -func (r *RedisReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RedisReconcileModel +func (r *NodejsRedeployResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NodejsRedeployModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - instanceUID := strings.TrimSpace(plan.RedisID.ValueString()) + instanceUID := strings.TrimSpace(plan.NodejsID.ValueString()) runID := strings.TrimSpace(plan.RunID.ValueString()) if instanceUID == "" || runID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") return } - params := map[string]string{} + params := map[string]string{ + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return } - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *RedisReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *NodejsRedeployResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // No-op: removing the resource does not trigger a remote action. } -func (r *RedisReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *NodejsRedeployResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } diff --git a/generated/prod/go/95_nodejs_resource.go b/generated/prod/go/95_nodejs_resource.go new file mode 100644 index 0000000..c6bc6c6 --- /dev/null +++ b/generated/prod/go/95_nodejs_resource.go @@ -0,0 +1,475 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nodejs +// Service ID: 95 + +var _ resource.Resource = &NodejsResource{} +var _ resource.ResourceWithModifyPlan = &NodejsResource{} +var _ resource.ResourceWithImportState = &NodejsResource{} + +type NodejsResource struct { + client *core.UniversalClient +} + +type NodejsModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + AppVersion types.String `tfsdk:"app_version"` + Domain types.String `tfsdk:"domain"` + GitPath types.String `tfsdk:"git_path"` + HealthPath types.String `tfsdk:"health_path"` + JsonEnv types.String `tfsdk:"json_env"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNodejsResource() resource.Resource { + return &NodejsResource{} +} + +func (r *NodejsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nodejs" +} + +func (r *NodejsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("23"), MarkdownDescription: "Версия приложения"}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testnode.git"), MarkdownDescription: "Путь до Git, который будет webroot для сайта"}, + "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет"}, + "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения"}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NodejsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NodejsModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NodejsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NodejsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 149: resources_core.FormatString(config.Domain), + 150: resources_core.FormatString(config.GitPath), + 151: resources_core.FormatString(config.HealthPath), + 152: resources_core.FormatString(config.JsonEnv), + 153: resources_core.FormatInt64(config.ResourceCPU), + 154: resources_core.FormatInt64(config.ResourceMemory), + 155: resources_core.FormatInt64(config.ResourceInstances), + 156: resources_core.FormatString(config.ResourceRealm), + 270: resources_core.FormatString(config.AppVersion), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 95, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NodejsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NodejsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 149: resources_core.FormatString(data.Domain), + 150: resources_core.FormatString(data.GitPath), + 151: resources_core.FormatString(data.HealthPath), + 152: resources_core.FormatString(data.JsonEnv), + 153: resources_core.FormatInt64(data.ResourceCPU), + 154: resources_core.FormatInt64(data.ResourceMemory), + 155: resources_core.FormatInt64(data.ResourceInstances), + 156: resources_core.FormatString(data.ResourceRealm), + 270: resources_core.FormatString(data.AppVersion), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 95, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NodejsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NodejsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 95, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NodejsModel + var state NodejsModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.GitPath.IsNull() != state.GitPath.IsNull() || plan.GitPath.IsUnknown() != state.GitPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.GitPath.IsNull() && !plan.GitPath.IsUnknown() && !state.GitPath.IsNull() && !state.GitPath.IsUnknown() { + if plan.GitPath.ValueString() != state.GitPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.HealthPath.IsNull() != state.HealthPath.IsNull() || plan.HealthPath.IsUnknown() != state.HealthPath.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.HealthPath.IsNull() && !plan.HealthPath.IsUnknown() && !state.HealthPath.IsNull() && !state.HealthPath.IsUnknown() { + if plan.HealthPath.ValueString() != state.HealthPath.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { + if plan.AppVersion.ValueString() != state.AppVersion.ValueString() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 157: resources_core.FormatString(plan.GitPath), + 158: resources_core.FormatString(plan.HealthPath), + 159: resources_core.FormatString(plan.JsonEnv), + 160: resources_core.FormatInt64(plan.ResourceCPU), + 161: resources_core.FormatInt64(plan.ResourceMemory), + 162: resources_core.FormatInt64(plan.ResourceInstances), + 271: resources_core.FormatString(plan.AppVersion), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 95, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "appVersion", Field: "AppVersion", Type: "string"}, + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "gitPath", Field: "GitPath", Type: "string"}, + {Code: "healthPath", Field: "HealthPath", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NodejsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NodejsModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NodejsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NodejsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/96_pgadmin_redeploy_action.go b/generated/prod/go/96_pgadmin_redeploy_action.go new file mode 100644 index 0000000..a5c1529 --- /dev/null +++ b/generated/prod/go/96_pgadmin_redeploy_action.go @@ -0,0 +1,141 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: pgadmin +// Action: redeploy + +var _ resource.Resource = &PgadminRedeployResource{} + +// PgadminRedeployResource triggers a one-time action. +type PgadminRedeployResource struct { + client *core.UniversalClient +} + +type PgadminRedeployModel struct { + ID types.String `tfsdk:"id"` + PgadminID types.String `tfsdk:"pgadmin_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ResourceRealm types.String `tfsdk:"resource_realm"` +} + +func NewPgadminRedeployResource() resource.Resource { + return &PgadminRedeployResource{} +} + +func (r *PgadminRedeployResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pgadmin_redeploy" +} + +func (r *PgadminRedeployResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "pgadmin_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PgadminRedeployResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan PgadminRedeployModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + instanceUID := strings.TrimSpace(plan.PgadminID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "redeploy", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PgadminRedeployResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PgadminRedeployModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PgadminRedeployResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PgadminRedeployModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.PgadminID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *PgadminRedeployResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *PgadminRedeployResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/96_pgadmin_resource.go b/generated/prod/go/96_pgadmin_resource.go new file mode 100644 index 0000000..83bc255 --- /dev/null +++ b/generated/prod/go/96_pgadmin_resource.go @@ -0,0 +1,449 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: pgadmin +// Service ID: 96 + +var _ resource.Resource = &PgadminResource{} +var _ resource.ResourceWithModifyPlan = &PgadminResource{} +var _ resource.ResourceWithImportState = &PgadminResource{} + +type PgadminResource struct { + client *core.UniversalClient +} + +type PgadminModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + Login types.String `tfsdk:"login"` + Password types.String `tfsdk:"password"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewPgadminResource() resource.Resource { + return &PgadminResource{} +} + +func (r *PgadminResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pgadmin" +} + +func (r *PgadminResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "login": schema.StringAttribute{Required: true, MarkdownDescription: "Логин для подключения к pgAdmin, в формате email (login@email.net) Не изменяется после создания."}, + "password": schema.StringAttribute{Required: true, MarkdownDescription: "Пароль для подключения к pgAdmin Не изменяется после создания.", Sensitive: true}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *PgadminModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *PgadminModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan PgadminModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Login.IsNull() && !plan.Login.IsUnknown() && !state.Login.IsNull() && !state.Login.IsUnknown() { + if plan.Login.ValueString() != state.Login.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить login", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Password.IsNull() && !plan.Password.IsUnknown() && !state.Password.IsNull() && !state.Password.IsUnknown() { + if plan.Password.ValueString() != state.Password.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить password", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Login.IsNull() || config.Login.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "login is required.") + return + } + if config.Password.IsNull() || config.Password.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "password is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 164: resources_core.FormatString(config.Domain), + 165: resources_core.FormatInt64(config.ResourceCPU), + 166: resources_core.FormatInt64(config.ResourceMemory), + 167: resources_core.FormatInt64(config.ResourceDisk), + 169: resources_core.FormatString(config.ResourceRealm), + 170: resources_core.FormatString(config.Login), + 171: resources_core.FormatString(config.Password), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 96, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *PgadminResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data PgadminModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Login.IsNull() || data.Login.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "login is required.") + return + } + if data.Password.IsNull() || data.Password.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "password is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 164: resources_core.FormatString(data.Domain), + 165: resources_core.FormatInt64(data.ResourceCPU), + 166: resources_core.FormatInt64(data.ResourceMemory), + 167: resources_core.FormatInt64(data.ResourceDisk), + 169: resources_core.FormatString(data.ResourceRealm), + 170: resources_core.FormatString(data.Login), + 171: resources_core.FormatString(data.Password), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 96, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "login", Field: "Login", Type: "string"}, + {Code: "password", Field: "Password", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PgadminResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PgadminModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 96, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "login", Field: "Login", Type: "string"}, + {Code: "password", Field: "Password", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *PgadminResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PgadminModel + var state PgadminModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 172: resources_core.FormatInt64(plan.ResourceCPU), + 173: resources_core.FormatInt64(plan.ResourceMemory), + 174: resources_core.FormatInt64(plan.ResourceDisk), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 96, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "login", Field: "Login", Type: "string"}, + {Code: "password", Field: "Password", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PgadminResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PgadminModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *PgadminResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *PgadminResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/97_nodered_resource.go b/generated/prod/go/97_nodered_resource.go new file mode 100644 index 0000000..0c16b24 --- /dev/null +++ b/generated/prod/go/97_nodered_resource.go @@ -0,0 +1,411 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: nodered +// Service ID: 97 + +var _ resource.Resource = &NoderedResource{} +var _ resource.ResourceWithModifyPlan = &NoderedResource{} +var _ resource.ResourceWithImportState = &NoderedResource{} + +type NoderedResource struct { + client *core.UniversalClient +} + +type NoderedModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewNoderedResource() resource.Resource { + return &NoderedResource{} +} + +func (r *NoderedResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_nodered" +} + +func (r *NoderedResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *NoderedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *NoderedModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *NoderedModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan NoderedModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Domain.IsNull() || config.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 193: resources_core.FormatInt64(config.ResourceCPU), + 194: resources_core.FormatInt64(config.ResourceMemory), + 195: resources_core.FormatInt64(config.ResourceDisk), + 196: resources_core.FormatString(config.ResourceRealm), + 197: resources_core.FormatString(config.Domain), + 323: resources_core.FormatInt64(config.ResourceInstances), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 97, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *NoderedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data NoderedModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Domain.IsNull() || data.Domain.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "domain is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 193: resources_core.FormatInt64(data.ResourceCPU), + 194: resources_core.FormatInt64(data.ResourceMemory), + 195: resources_core.FormatInt64(data.ResourceDisk), + 196: resources_core.FormatString(data.ResourceRealm), + 197: resources_core.FormatString(data.Domain), + 323: resources_core.FormatInt64(data.ResourceInstances), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 97, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NoderedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state NoderedModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 97, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan NoderedModel + var state NoderedModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{} + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 97, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *NoderedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state NoderedModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "delete" + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *NoderedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *NoderedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/98_http_redeploy_action.go b/generated/prod/go/98_http_redeploy_action.go new file mode 100644 index 0000000..4879187 --- /dev/null +++ b/generated/prod/go/98_http_redeploy_action.go @@ -0,0 +1,141 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: http +// Action: redeploy + +var _ resource.Resource = &HttpRedeployResource{} + +// HttpRedeployResource triggers a one-time action. +type HttpRedeployResource struct { + client *core.UniversalClient +} + +type HttpRedeployModel struct { + ID types.String `tfsdk:"id"` + HttpID types.String `tfsdk:"http_id"` + RunID types.String `tfsdk:"run_id"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + ResourceRealm types.String `tfsdk:"resource_realm"` +} + +func NewHttpRedeployResource() resource.Resource { + return &HttpRedeployResource{} +} + +func (r *HttpRedeployResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_http_redeploy" +} + +func (r *HttpRedeployResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "http_id": schema.StringAttribute{Required: true}, + "run_id": schema.StringAttribute{Required: true}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания"}, + } + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *HttpRedeployResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan HttpRedeployModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.ResourceRealm.IsNull() || plan.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + instanceUID := strings.TrimSpace(plan.HttpID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "redeploy", params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *HttpRedeployResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state HttpRedeployModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HttpRedeployResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan HttpRedeployModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + instanceUID := strings.TrimSpace(plan.HttpID.ValueString()) + runID := strings.TrimSpace(plan.RunID.ValueString()) + if instanceUID == "" || runID == "" { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") + return + } + + params := map[string]string{ + "resourceRealm": resources_core.FormatString(plan.ResourceRealm), + } + + if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "redeploy", params); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "redeploy", runID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *HttpRedeployResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // No-op: removing the resource does not trigger a remote action. +} + +func (r *HttpRedeployResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/98_http_resource.go b/generated/prod/go/98_http_resource.go new file mode 100644 index 0000000..9f113f1 --- /dev/null +++ b/generated/prod/go/98_http_resource.go @@ -0,0 +1,433 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: http +// Service ID: 98 + +var _ resource.Resource = &HttpResource{} +var _ resource.ResourceWithModifyPlan = &HttpResource{} +var _ resource.ResourceWithImportState = &HttpResource{} + +type HttpResource struct { + client *core.UniversalClient +} + +type HttpModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + JsonEnv types.String `tfsdk:"json_env"` + RegistryPath types.String `tfsdk:"registry_path"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewHttpResource() resource.Resource { + return &HttpResource{} +} + +func (r *HttpResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_http" +} + +func (r *HttpResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{"param": "value"}"), MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания."}, + "registry_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nexus-sa.tst.nubes.ru/docker-nubes/jolt"), MarkdownDescription: "Путь до image, который будет установлен как deployment Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *HttpModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *HttpModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan HttpModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + if plan.JsonEnv.ValueString() != state.JsonEnv.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить json_env", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RegistryPath.IsNull() && !plan.RegistryPath.IsUnknown() && !state.RegistryPath.IsNull() && !state.RegistryPath.IsUnknown() { + if plan.RegistryPath.ValueString() != state.RegistryPath.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить registry_path", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + if plan.ResourceRealm.ValueString() != state.ResourceRealm.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() || config.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 212: resources_core.FormatString(config.Domain), + 213: resources_core.FormatInt64(config.ResourceCPU), + 214: resources_core.FormatInt64(config.ResourceMemory), + 215: resources_core.FormatInt64(config.ResourceInstances), + 216: resources_core.FormatString(config.ResourceRealm), + 217: resources_core.FormatString(config.RegistryPath), + 241: resources_core.FormatString(config.JsonEnv), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 98, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data HttpModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 212: resources_core.FormatString(data.Domain), + 213: resources_core.FormatInt64(data.ResourceCPU), + 214: resources_core.FormatInt64(data.ResourceMemory), + 215: resources_core.FormatInt64(data.ResourceInstances), + 216: resources_core.FormatString(data.ResourceRealm), + 217: resources_core.FormatString(data.RegistryPath), + 241: resources_core.FormatString(data.JsonEnv), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 98, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "registryPath", Field: "RegistryPath", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HttpResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state HttpModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 98, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "registryPath", Field: "RegistryPath", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *HttpResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan HttpModel + var state HttpModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 218: resources_core.FormatInt64(plan.ResourceCPU), + 219: resources_core.FormatInt64(plan.ResourceMemory), + 220: resources_core.FormatInt64(plan.ResourceInstances), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 98, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "registryPath", Field: "RegistryPath", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HttpResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state HttpModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *HttpResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *HttpResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/generated/prod/go/99_gitea_resource.go b/generated/prod/go/99_gitea_resource.go new file mode 100644 index 0000000..e6f677b --- /dev/null +++ b/generated/prod/go/99_gitea_resource.go @@ -0,0 +1,482 @@ +package resources_gen + +import ( + "context" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: gitea +// Service ID: 99 + +var _ resource.Resource = &GiteaResource{} +var _ resource.ResourceWithModifyPlan = &GiteaResource{} +var _ resource.ResourceWithImportState = &GiteaResource{} + +type GiteaResource struct { + client *core.UniversalClient +} + +type GiteaModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + Domain types.String `tfsdk:"domain"` + PsqlUid types.String `tfsdk:"psql_uid"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewGiteaResource() resource.Resource { + return &GiteaResource{} +} + +func (r *GiteaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_gitea" +} + +func (r *GiteaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *GiteaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *GiteaModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *GiteaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan GiteaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + planChanged := false + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, plan.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != plan.PsqlUid.ValueString() { + plan.PsqlUid = types.StringValue(resolvedPsqlUid) + planChanged = true + } + } + if planChanged { + resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + if plan.Domain.ValueString() != state.Domain.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + if plan.PsqlUid.ValueString() != state.PsqlUid.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.PsqlUid.IsNull() || config.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { + config.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + params := map[int]string{ + 232: resources_core.FormatInt64(config.ResourceCPU), + 233: resources_core.FormatInt64(config.ResourceMemory), + 234: resources_core.FormatInt64(config.ResourceDisk), + 235: resources_core.FormatInt64(config.ResourceInstances), + 237: resources_core.FormatString(config.Domain), + 238: resources_core.FormatString(config.PsqlUid), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 99, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *GiteaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data GiteaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") + return + } + if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { + data.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{2, 50, 81, 82, 88, 89, 94, 95, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 232: resources_core.FormatInt64(data.ResourceCPU), + 233: resources_core.FormatInt64(data.ResourceMemory), + 234: resources_core.FormatInt64(data.ResourceDisk), + 235: resources_core.FormatInt64(data.ResourceInstances), + 237: resources_core.FormatString(data.Domain), + 238: resources_core.FormatString(data.PsqlUid), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 99, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *GiteaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state GiteaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 99, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { + newState.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan GiteaModel + var state GiteaModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + + if !hasServiceParamChanges { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + params := map[int]string{ + 259: resources_core.FormatInt64(plan.ResourceCPU), + 260: resources_core.FormatInt64(plan.ResourceMemory), + 261: resources_core.FormatInt64(plan.ResourceDisk), + 262: resources_core.FormatInt64(plan.ResourceInstances), + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 99, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { + resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) + if err != nil { + resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) + } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { + state.PsqlUid = types.StringValue(resolvedPsqlUid) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *GiteaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state GiteaModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *GiteaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *GiteaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/registry.go b/generated/prod/go/registry.go similarity index 69% rename from provider/internal/resources_gen/registry.go rename to generated/prod/go/registry.go index 484bb51..183b8c8 100644 --- a/provider/internal/resources_gen/registry.go +++ b/generated/prod/go/registry.go @@ -50,50 +50,27 @@ func AllResources() []func() resource.Resource { NewVmpostgreResource, NewClickhouseDatabaseResource, NewClickhouseUserResource, - NewK8sSthutrvalClusterUserResource, NewKafkaTopicResource, NewKafkaUserResource, - NewMariadbDatabaseResource, - NewMariadbUserResource, NewMongodbUserResource, - NewNextcloudBackupResource, - NewPostgresBackupResource, - NewPostgresDatabaseResource, - NewPostgresUserResource, NewS3SubUserResource, - NewVcOrgUserResource, - NewVcVdcGroupVdcResource, - NewClickhouseReconcileResource, - NewDnsrecordReconcileResource, - NewDummyReconcileResource, NewDummyRedeployResource, - NewFlaskReconcileResource, - NewFlaskRedeployResource, - NewHttpReconcileResource, NewHttpRedeployResource, NewK8sSthutrvalClusterReconcileResource, - NewLuceeReconcileResource, + NewK8sSthutrvalClusterUpdateSecretsResource, NewLuceeRedeployResource, NewLuceeRestartResource, - NewMariadbReconcileResource, - NewNextcloudReconcileResource, - NewNextcloudRecoveryResource, NewNextcloudRestartResource, - NewNodejsReconcileResource, NewNodejsRedeployResource, NewPgadminRedeployResource, - NewPostgresReconcileResource, NewPostgresRecoveryResource, NewPostgresRestartResource, - NewRedisReconcileResource, - NewS3ReconcileResource, - NewTemplateReconcileResource, - NewVappReconcileResource, NewVcNsxtReconcileResource, NewVcOrgReconcileResource, NewVcVdcReconcileResource, + NewVcVdcGroupAddVdcResource, NewVcVdcGroupReconcileResource, - NewVcVmV3ReconcileResource, + NewVcVdcGroupRemoveVdcResource, NewVcVmV3RedeployResource, } } diff --git a/generated/prod/json/.gitkeep b/generated/prod/json/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/prod/resources_yaml/100_openwhisk.yaml b/generated/prod/resources_yaml/100_openwhisk.yaml new file mode 100644 index 0000000..88b1ee9 --- /dev/null +++ b/generated/prod/resources_yaml/100_openwhisk.yaml @@ -0,0 +1,54 @@ +name: openwhisk +service_id: 100 +service_display_name: Serverless Openwhisk (openwhisk_tenant) +service_short_name: openwhisk +service_man: Добавление и удаления пользователя для работы с Openwhisk +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 41 + kind: instance + action: create + params: + - id: 64 + code: username + data_type: string + required: true + descr: Имя пользователя Openwhisk + - id: 863 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Ресурсная платформа + - name: delete + id: 42 + kind: instance + action: delete + params: + - id: 65 + code: username + data_type: string + required: true + descr: Имя пользователя Openwhisk diff --git a/generated/prod/resources_yaml/110_dnszone.yaml b/generated/prod/resources_yaml/110_dnszone.yaml new file mode 100644 index 0000000..03d2540 --- /dev/null +++ b/generated/prod/resources_yaml/110_dnszone.yaml @@ -0,0 +1,94 @@ +name: dnszone +service_id: 110 +service_display_name: DNS зона +service_short_name: dnszone +service_man: '## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
```' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 43 + kind: instance + action: create + params: + - id: 66 + code: zoneEmail + data_type: string + required: true + descr: Контактный e-mail + - id: 67 + code: zoneName + data_type: string + required: true + regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ + descr: Имя зоны + man: Имя зоны + - id: 68 + code: zoneKind + data_type: string + required: true + default: NATIVE + value_list: + - NATIVE + - SLAVE + descr: Тип зоны + - id: 69 + code: zoneMastersForSlave + data_type: string + required: false + descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов + - id: 70 + code: zoneTimeToRefresh + data_type: string + required: true + default: "86400" + descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов + - id: 71 + code: zoneTimeToRetry + data_type: string + required: true + default: "7200" + descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов + - id: 72 + code: zoneTimeToExpire + data_type: string + required: true + default: "3600000" + descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным + - id: 73 + code: zoneTimeToLive + data_type: string + required: true + default: "86400" + descr: Время, в течение которого информация будет кешироваться другими DNS-серверами + - id: 284 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: delete + id: 44 + kind: instance + action: delete + params: [] diff --git a/generated/prod/resources_yaml/111_dnsrecord.yaml b/generated/prod/resources_yaml/111_dnsrecord.yaml new file mode 100644 index 0000000..4efad14 --- /dev/null +++ b/generated/prod/resources_yaml/111_dnsrecord.yaml @@ -0,0 +1,100 @@ +name: dnsrecord +service_id: 111 +service_display_name: DNS запись +service_short_name: dnsrecord +service_man: Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 45 + kind: instance + action: create + params: + - id: 75 + code: zoneUid + data_type: uuid + required: true + ref_svc_id: 110 + descr: UUID экземпляра услуги DNS-зоны + man: UUID экземпляра услуги DNS-зоны + sort: 10 + - id: 76 + code: recordType + data_type: string + required: true + value_list: + - A + - AAAA + - CNAME + - MX + - PTR + - SOA + - SRV + - TXT + - LUA + - NS + - SSHFP + descr: Тип DNS-записи + man: Тип DNS-записи + sort: 20 + - id: 77 + code: recordName + data_type: string + required: true + unique_scope: parent + descr: Имя записи. Имя зоны в конце проставляется автоматически + man: Имя записи. Имя зоны в конце проставляется автоматически + sort: 30 + - id: 78 + code: recordInput + data_type: string + required: true + descr: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP + man: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP + sort: 40 + is_modifiable: true + - id: 450 + code: recordTTL + data_type: integer > 0 + required: true + default: "3600" + maxvalue: 604800 + minvalue: 30 + descr: Time-to-Leave записи. Раз в какое время запись будет обновлена + sort: 50 + - name: delete + id: 46 + kind: instance + action: delete + params: [] + - name: modify + id: 90 + kind: instance + action: modify + params: + - id: 256 + code: recordInput + data_type: string + required: true + descr: Запись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP diff --git a/provider/resources_yaml/112_tenant.yaml b/generated/prod/resources_yaml/112_tenant.yaml similarity index 100% rename from provider/resources_yaml/112_tenant.yaml rename to generated/prod/resources_yaml/112_tenant.yaml diff --git a/provider/resources_yaml/113_vc_complex.yaml b/generated/prod/resources_yaml/113_vc_complex.yaml similarity index 100% rename from provider/resources_yaml/113_vc_complex.yaml rename to generated/prod/resources_yaml/113_vc_complex.yaml diff --git a/provider/resources_yaml/114_gitea_complex.yaml b/generated/prod/resources_yaml/114_gitea_complex.yaml similarity index 100% rename from provider/resources_yaml/114_gitea_complex.yaml rename to generated/prod/resources_yaml/114_gitea_complex.yaml diff --git a/generated/prod/resources_yaml/115_mariadb.yaml b/generated/prod/resources_yaml/115_mariadb.yaml new file mode 100644 index 0000000..7fbd969 --- /dev/null +++ b/generated/prod/resources_yaml/115_mariadb.yaml @@ -0,0 +1,247 @@ +name: mariadb +service_id: 115 +service_display_name: Mariadb +service_short_name: mariadb +service_man: 'Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 138 + kind: instance + action: create + params: + - id: 422 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 10 + - id: 424 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 500 + descr: Квота ядра пода (milicores) + sort: 30 + is_modifiable: true + - id: 425 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 1024 + descr: Квота памяти пода (MBytes) + sort: 30 + is_modifiable: true + - id: 426 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 427 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 60 + is_modifiable: true + - id: 428 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к master + sort: 70 + is_modifiable: true + - id: 429 + code: ipSpaceNameMaster + data_type: string + required: false + descr: Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster + sort: 80 + is_modifiable: true + - id: 430 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + default: 0 * * * * + regex: ^(\*|(\d+|\*\/\d+|\d+(,\d+)*))\s\*\s\*\s\*\s\*$ + descr: Не обязательный. Время запуска бекапа + sort: 90 + is_modifiable: true + - id: 431 + code: appVersion + data_type: string + required: true + default: 9.4.0 + value_list: + - 8.4.0 + - 9.4.0 + descr: Версия MySQL + sort: 100 + - id: 432 + code: autoScale + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + sort: 310 + is_modifiable: true + - id: 433 + code: autoScalePercentage + data_type: integer > 0 + required: false + default: "10" + value_list: + - "10" + - "15" + - "20" + descr: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + sort: 320 + is_modifiable: true + - id: 434 + code: autoScaleTechWindow + data_type: integer >= 0 + required: false + default: "0" + value_list: + - "0" + - "5" + - "12" + - "23" + descr: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + sort: 330 + is_modifiable: true + - id: 435 + code: autoScaleQuotaGb + data_type: integer > 0 + required: false + default: "1" + descr: Квота для autoScale + sort: 340 + is_modifiable: true + - id: 459 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + sort: 20 + - name: delete + id: 139 + kind: instance + action: delete + params: + - id: 456 + code: deleteS3Bucket + data_type: boolean + required: true + default: "true" + value_list: + - "false" + - "true" + descr: Также удалить технический S3 Bucket с бэкапами? + sort: 10 + - name: modify + id: 142 + kind: instance + action: modify + params: + - id: 436 + code: resourceCPU + data_type: integer > 0 + required: false + - id: 437 + code: resourceMemory + data_type: integer > 0 + required: false + - id: 438 + code: resourceInstances + data_type: integer > 0 + required: false + - id: 439 + code: resourceDisk + data_type: integer > 0 + required: false + - id: 440 + code: needExternalAddressMaster + data_type: boolean + required: false + value_list: + - "false" + - "true" + - id: 441 + code: ipSpaceNameMaster + required: false + - id: 442 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + - id: 443 + code: autoScale + data_type: boolean + required: false + value_list: + - "false" + - "true" + - id: 444 + code: autoScalePercentage + data_type: integer > 0 + required: false + - id: 445 + code: autoScaleTechWindow + data_type: integer >= 0 + required: false + - id: 446 + code: autoScaleQuotaGb + data_type: integer > 0 + required: false + - name: resume + id: 141 + kind: instance + action: resume + params: [] + - name: suspend + id: 140 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/116_kafka.yaml b/generated/prod/resources_yaml/116_kafka.yaml new file mode 100644 index 0000000..d2f3408 --- /dev/null +++ b/generated/prod/resources_yaml/116_kafka.yaml @@ -0,0 +1,291 @@ +name: kafka +service_id: 116 +service_display_name: ApacheKafka +service_short_name: kafka +service_man: "# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
Брокер сообщений Apache Kafka — это отказоустойчивая распределённая платформа для обмена сообщениями и потоковой обработки данных в реальном времени.
Позволяет надёжно передавать события между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `\tk8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов (брокеров) в кластере Kafka

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**internalConnect**
Это внутренний эндпоинт, который используется для подключения к кластеру Kafka из других сервисов, развернутых внутри той же сети.
Эндпоинт предоставляет доступ к брокерам Kafka по внутренним портам, указанным в конфигурации слушателей.

**externalConnect**
Это внешний эндпоинт (fqdn), который используется для подключения к кластеру Kafka. Для подключения используеться 9093 порт.

Для подключения используются следующие порты:

* 9092 — внутренний нешифрованный доступ (plain).
* 9093 — внутренний и внешний доступ по TLS, с обязательной TLS-аутентификацией.

---
## 5. Дополнительная информация
Для создания топиков, пользователей и управления ими, а также для интеграции с кластерами Kafka используйте соответствующие сервисы и раздел "Облачные сервисы".
---" +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 148 + kind: instance + action: create + params: + - id: 461 + code: resourceInstances + data_type: integer > 0 + required: true + value_list: + - "1" + - "2" + - "3" + - "4" + - "5" + minvalue: 1 + descr: 'Пример: `2`' + sort: 20 + is_modifiable: true + - id: 462 + code: resourceMemory + data_type: integer > 0 + required: true + minvalue: 1000 + descr: 'Пример: `2048`' + sort: 30 + is_modifiable: true + - id: 463 + code: resourceCPU + data_type: integer > 0 + required: true + minvalue: 1000 + descr: 'Пример: `2000`' + sort: 40 + is_modifiable: true + - id: 464 + code: resourceDisk + data_type: integer > 0 + required: true + descr: 'Пример: `20`' + sort: 50 + is_modifiable: true + - id: 465 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`' + sort: 60 + is_modifiable: true + - id: 466 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' + sort: 70 + is_modifiable: true + - id: 469 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 5 + - name: create_topic + id: 165 + kind: subresource + action: create + subresource: topic + man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' + params: + - id: 523 + code: nameTopic + data_type: string + required: true + descr: 'Пример: `orders-events`' + sort: 10 + - id: 524 + code: partitions + data_type: integer > 0 + required: true + descr: Число партиций можно увеличить, но уменьшить их нельзя. + - id: 525 + code: replicas + data_type: integer > 0 + required: true + descr: Количество реплик не может привышать количество реплик брокера + sort: 20 + - name: create_user + id: 168 + kind: subresource + action: create + subresource: user + man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' + params: + - id: 530 + code: username + data_type: string + required: true + descr: 'Пример: `username-admin`' + - id: 531 + code: nameTopic + data_type: string + required: true + descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' + - id: 532 + code: operations + data_type: string + required: true + descr: 'Пример: `Create, Describe, Read, Write`' + - id: 533 + code: group + data_type: string + required: true + descr: 'Пример: `order-consumers` или `*`' + - id: 534 + code: accessHosts + data_type: string + required: true + descr: 'Пример: `192.168.1.0/24` или `*`' + - name: delete + id: 149 + kind: instance + action: delete + params: [] + - name: delete_topic + id: 167 + kind: subresource + action: delete + subresource: topic + params: + - id: 529 + code: nameTopic + data_type: string + required: true + descr: Необходимо выбрать топик, который необходимо удалить + - name: delete_user + id: 170 + kind: subresource + action: delete + subresource: user + man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' + params: + - id: 540 + code: username + data_type: string + required: true + descr: 'Пример: `username-admin`' + - name: modify + id: 162 + kind: instance + action: modify + params: + - id: 501 + code: resourceInstances + data_type: integer > 0 + required: false + value_list: + - "1" + - "2" + - "3" + - "4" + - "5" + descr: 'Пример: `2`' + - id: 502 + code: resourceMemory + data_type: integer > 0 + required: false + minvalue: 1000 + descr: 'Пример: `2048`' + - id: 503 + code: resourceCPU + required: false + minvalue: 1000 + descr: 'Пример: `2000`' + - id: 504 + code: resourceDisk + data_type: integer > 0 + required: false + descr: 'Пример: `20`' + - id: 505 + code: needExternalAddressMaster + data_type: boolean + required: false + default: "false" + value_list: + - "false" + - "true" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`' + - id: 506 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' + - name: modify_topic + id: 166 + kind: subresource + action: modify + subresource: topic + man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' + params: + - id: 526 + code: nameTopic + data_type: string + required: true + descr: 'Пример: `orders-events`' + sort: 10 + - id: 527 + code: partitions + required: false + descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` + sort: 30 + - id: 528 + code: replicas + required: false + descr: Количество партиций ≤ (число брокеров). Пример `2` + sort: 20 + - name: modify_user + id: 169 + kind: subresource + action: modify + subresource: user + man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' + params: + - id: 535 + code: nameTopic + data_type: string + required: false + descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' + - id: 536 + code: operations + data_type: string + required: false + descr: 'Пример: `Create, Describe, Read, Write`' + - id: 537 + code: group + data_type: string + required: false + descr: 'Пример: `order-consumers` или `*`' + - id: 538 + code: accessHosts + data_type: string + required: false + descr: 'Пример: `192.168.1.0/24` или `*`' + - id: 539 + code: username + data_type: string + required: true + descr: 'Пример: `username-admin`' + sort: 10 + - name: resume + id: 161 + kind: instance + action: resume + params: [] + - name: suspend + id: 160 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/117_nifi.yaml b/generated/prod/resources_yaml/117_nifi.yaml similarity index 100% rename from provider/resources_yaml/117_nifi.yaml rename to generated/prod/resources_yaml/117_nifi.yaml diff --git a/generated/prod/resources_yaml/119_akhq.yaml b/generated/prod/resources_yaml/119_akhq.yaml new file mode 100644 index 0000000..7339976 --- /dev/null +++ b/generated/prod/resources_yaml/119_akhq.yaml @@ -0,0 +1,104 @@ +name: akhq +service_id: 119 +service_display_name: Akhq +service_short_name: Akhq +service_man: '# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group''ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
---' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 175 + kind: instance + action: create + params: + - id: 550 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 10 + - id: 551 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + minvalue: 512 + descr: 'Пример: `1024`' + sort: 20 + - id: 552 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + minvalue: 500 + descr: 'Пример: `1000`' + sort: 30 + - id: 553 + code: domain + data_type: string + required: true + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 40 + - id: 554 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + minvalue: 1 + sort: 11 + - id: 555 + code: kafkaUid + data_type: uuid + required: true + ref_svc_id: 116 + descr: Выбрать кластер из списка + sort: 5 + is_modifiable: true + - name: delete + id: 176 + kind: instance + action: delete + params: [] + - name: modify + id: 180 + kind: instance + action: modify + params: + - id: 565 + code: kafkaUid + data_type: uuid + required: true + ref_svc_id: 116 + descr: Выбрать кластер из списка + - name: resume + id: 178 + kind: instance + action: resume + params: [] + - name: suspend + id: 177 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/120_clickhouse.yaml b/generated/prod/resources_yaml/120_clickhouse.yaml new file mode 100644 index 0000000..efae3cf --- /dev/null +++ b/generated/prod/resources_yaml/120_clickhouse.yaml @@ -0,0 +1,417 @@ +name: clickhouse +service_id: 120 +service_display_name: ClickHouse +service_short_name: clickhouse +service_man: '# Инструкция по управлению кластерами и пользователями ClickHouse

---

## Общая информация

ClickHouse — это колоночная система управления базами данных, предназначенная для обработки аналитических запросов в реальном времени. Платформа позволяет создавать, удалять, приостанавливать и возобновлять кластеры ClickHouse, а также управлять базами данных и пользователями.

Кластер создаётся через оператор Altinity (`https://github.com/Altinity/clickhouse-operator`) в ресурсной платформе типа `k8s`

Кластеры могут создаваться шардированными с произвольным количеством реплик. Для обеспечения отказоустойчивости и координации обязательно включение параметра `Создать ClickHouse-Keeper`.

Кластера создаются с обязательным бекапом. Для этого необходимо предварительно создать услугу `S3 Object Storage`.
---

## Доступные операции

* **create** — Создание кластера
* **delete** — Удаление кластера
* **suspend** — Приостановка работы кластера
* **resume** — Возобновление работы кластера
* **create_database** — Создание базы данных
* **delete_database** — Удаление базы данных
* **create_user** — Создание пользователя и настройка прав доступа к базе данных
* **delete_user** — Удаление пользователя

## Процесс создания и первого подключения

1. Выполнить операцию `create` - будет создан кластер
2. Выполнить операцию `create_database` - будет создана база данных
3. Выполнить операцию `create_user` - будет создан пользователь (выбрать доступ подключения к БД, созданной в п.2)

## Примеры подключения

### Подключение через HTTP-интерфейс

```bash
# Проверка доступности кластера
curl "http://<username>:<password>@<host>:8123/" -d "SELECT 1"

# Выполнение запроса
curl "http://<username>:<password>@<host>:8123/" -d "SHOW DATABASES"
```

## Простые кейсы
*Создать БД и обычной таблицы*
```clickhouse
CREATE TABLE database.table
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

INSERT INTO database.table SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM database.table;
```
Базы и таблицы не реплицируются без специального ключа (`ON CLUSTER ''clickhousek8s''`)
В случае создания в стиле, описанным выше, необходимо будет выполнять действия на всех шардах/репликах


*Создание БД и шардированной таблицы*
```clickhouse
CREATE TABLE database.localUsers
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

CREATE TABLE database.shardedTable
AS localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', id);

INSERT INTO shardedTable SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM shardedTable;
```
Важный момент - в данном примере таблицы придётся также выполнять на всех нодах ClickHouse

*Создание реплицируемой и шардированной таблицы*
```clickhouse
CREATE TABLE IF NOT EXISTS database.localUsers ON cluster ''clickhousek8s''
(
user_id UInt64,
name String,
email String
)
ENGINE = ReplicatedMergeTree(
''/clickhouse/tables/{shard}/localUsers'',
''{replica}''
)
ORDER BY user_id;

CREATE TABLE IF NOT EXISTS database.users ON cluster ''clickhousek8s''
AS database.localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', rand());

INSERT INTO database.users VALUES (1, ''Alice'', ''alice@example.com'');
INSERT INTO database.users VALUES (2, ''Bob'', ''bob@example.com'');
INSERT INTO database.users VALUES (3, ''Charlie'', ''charlie@example.com'');
INSERT INTO database.users VALUES (4, ''David'', ''david@example.com'');
INSERT INTO database.users VALUES (5, ''Gosha'', ''gosha@example.com'');
INSERT INTO database.users VALUES (6, ''Eve'', ''eve@example.com'');
INSERT INTO database.users VALUES (7, ''Frank'', ''frank@example.com'');
INSERT INTO database.users VALUES (8, ''Grace'', ''grace@example.com'');
INSERT INTO database.users VALUES (9, ''Hannah'', ''hannah@example.com'');
INSERT INTO database.users VALUES (10, ''Ivan'', ''ivan@example.com'');

SELECT * FROM database.localUsers;
SELECT * FROM database.users;

SELECT COUNT(*) AS total_users FROM database.users;
SELECT COUNT(*) AS total_users_local FROM database.localUsers;
```' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 181 + kind: instance + action: create + man: Операция создания кластера ClickHouse

Для подключения к кластеру также необходимо создать базу данных через операцию `create_database` и создание пользователя через операцию `create_user` + params: + - id: 572 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Кластер Kubernetes в котором будет развёрнуто приложение + man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы + sort: 10 + - id: 573 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + man: '**Если список не отображается**
перейдите в раздел `S3 Object Storage`
операция `create`' + sort: 20 + - id: 574 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + man: '**Для синхронизации данных между репликами** необходимо включить `Создать ClickHouse-Keeper`' + sort: 30 + is_modifiable: true + - id: 575 + code: resourceInstancesShards + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' + sort: 40 + is_modifiable: true + - id: 576 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 8000 + minvalue: 1000 + descr: 'Пример: `1000`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 50 + is_modifiable: true + - id: 577 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1536" + maxvalue: 8192 + minvalue: 1536 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 60 + is_modifiable: true + - id: 578 + code: appVersion + data_type: string + required: true + default: "25.10" + value_list: + - "25.10" + descr: Версия `clickhouse-server` + sort: 70 + is_modifiable: true + - id: 579 + code: resourceDisk + data_type: integer > 0 + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `100`' + man: Указывается в Gigabytes + sort: 80 + is_modifiable: true + - id: 580 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Позволяет подключиться извне к ClickHouse + man: Для выделения IP также необходимо указать имя Ip-space.
Его можно получить из свойств ресурсной платформы + sort: 90 + is_modifiable: true + - id: 581 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Пример: `internet-no-antiddos-v1`' + man: Имя ipSpace для публикации внешнего адреса

Не может быть пустым, если включен `Выделение белого ip для master ноды` + sort: 100 + is_modifiable: true + - id: 582 + code: needCreateKeeper + data_type: boolean + required: true + default: "true" + value_list: + - "false" + - "true" + descr: Необходим для кластеризации ClickHouse + man: Аналог `Zookeeper`, представленный от ClickHouse + sort: 200 + is_modifiable: true + - id: 583 + code: resourceCPUKeeper + data_type: integer > 0 + required: true + default: "50" + maxvalue: 8000 + minvalue: 50 + descr: 'Пример: `100`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 210 + is_modifiable: true + - id: 584 + code: resourceMemoryKeeper + data_type: integer > 0 + required: true + default: "152" + maxvalue: 8192 + minvalue: 152 + descr: 'Пример: `256`' + man: Указывается в Megabytes + sort: 220 + is_modifiable: true + - id: 585 + code: resourceInstancesKeeper + data_type: integer > 0 + required: true + default: "1" + value_list: + - "1" + - "3" + maxvalue: 3 + minvalue: 1 + descr: 'Пример: `1`' + man: Для **теста** - 1 нода.
Для **продуктива** - 3 ноды + sort: 230 + is_modifiable: true + - id: 586 + code: ext_BACKUP_SCHEDULE + data_type: string + required: true + default: 0 0 * * * + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + descr: Принимает формат `CRON` + sort: 500 + is_modifiable: true + - id: 587 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: true + default: "7" + maxvalue: 60 + minvalue: 3 + descr: 'Пример: `7`' + man: Кол-во **полных** резервных копий
Кол-во инкрементов - неограничено

Полная резервная копия выполняется раз в неделю
*Если изначально полной резервной копии нет* выполняется полный бекап
В остальные дни по крону выполняются **инкременты** + sort: 510 + is_modifiable: true + - name: create_database + id: 186 + kind: subresource + action: create + subresource: database + man: База данных создаётся на всех репликах кластера ClickHouse + params: + - id: 600 + code: databaseName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + descr: 'Пример: `db01`' + sort: 10 + - name: create_user + id: 185 + kind: subresource + action: create + subresource: user + params: + - id: 596 + code: username + data_type: string + required: true + regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ + maxlength: 62 + minlength: 2 + descr: 'Пример: `myusername`' + sort: 10 + - id: 598 + code: databaseName + data_type: json + required: true + default: '["database"]' + descr: Необходимо указать массив существующих баз + man: Если базы не существует - при выполнении операции вернётся ошибка + sort: 30 + - id: 599 + code: accessHosts + data_type: string + required: true + default: ::/0 + descr: 'Пример: `::/0`' + sort: 40 + - name: delete + id: 184 + kind: instance + action: delete + man: Удаление возможно только после остановки сервиса через операцию `suspend`
После выполнения операции необходимо выждать определенное кол-во времени + params: [] + - name: delete_database + id: 188 + kind: subresource + action: delete + subresource: database + man: Необратимое действие!!!
Трижды подумай, заблокируй телефон тимлида и не выполняй это действие + params: + - id: 602 + code: databaseName + data_type: string + required: true + descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ + - name: delete_user + id: 187 + kind: subresource + action: delete + subresource: user + params: + - id: 601 + code: username + required: true + descr: Имя пользователя, созданное через операцию `create_user` + - name: modify + id: 189 + kind: instance + action: modify + params: + - id: 603 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `3`' + man: '**Для синхронизации данных между репликами** необходимо включить `Создать ClickHouse-Keeper`' + sort: 10 + - id: 604 + code: resourceInstancesShards + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' + sort: 20 + - id: 605 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 8000 + minvalue: 1000 + descr: 'Пример: `1000`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 30 + - id: 606 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 1536 + descr: 'Пример: `2048`' + man: Указывается в Megabytes + sort: 40 + - id: 607 + code: appVersion + data_type: string + required: true + default: "25.10" + descr: Версия `clickhouse-server` + sort: 50 + - id: 608 + code: resourceDisk + data_type: integer > 0 + required: true + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `100`' + man: Указывается в Gigabytes
**Нельзя** выставлять меньше текущего значения + sort: 60 + - id: 609 + code: needExternalAddressMaster + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Позволяет подключиться извне к ClickHouse + man: Для выделения IP также необходимо указать имя Ip-space.
Его можно получить из свойств ресурсной платформы + sort: 70 + - id: 610 + code: ipSpaceNameMaster + data_type: string + required: true + descr: 'Пример: `internet-no-antiddos-v1`' + man: Имя ipSpace для публикации внешнего адреса

Не может быть пустым, если включен `Выделение белого ip для master ноды` + sort: 80 + - id: 611 + code: needCreateKeeper + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Необходим для кластеризации ClickHouse + man: Аналог `Zookeeper`, представленный от ClickHouse + sort: 200 + - id: 612 + code: resourceCPUKeeper + data_type: integer > 0 + required: true + maxvalue: 8000 + minvalue: 50 + descr: 'Пример: `100`' + man: Указывается в Milicores
`1000` Mili == `1` Ядро + sort: 210 + - id: 613 + code: resourceMemoryKeeper + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 152 + descr: 'Пример: `256`' + man: Указывается в Megabytes + sort: 220 + - id: 614 + code: resourceInstancesKeeper + data_type: integer > 0 + required: true + value_list: + - "1" + - "3" + maxvalue: 3 + minvalue: 1 + descr: 'Пример: `1`' + man: Для **теста** - 1 нода.
Для **продуктива** - 3 ноды + sort: 230 + - id: 615 + code: ext_BACKUP_SCHEDULE + data_type: string + required: true + default: 0 0 * * * + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + descr: Принимает формат `CRON` + sort: 500 + - id: 616 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: true + maxvalue: 60 + minvalue: 3 + descr: 'Пример: `7`' + man: Кол-во **полных** резервных копий
Кол-во инкрементов - неограничено

Полная резервная копия выполняется раз в неделю
*Если изначально полной резервной копии нет* выполняется полный бекап
В остальные дни по крону выполняются **инкременты** + sort: 510 + - name: resume + id: 183 + kind: instance + action: resume + params: [] + - name: suspend + id: 182 + kind: instance + action: suspend + man: Операция приводит к удалению всех подов
Persistent Volume остаются нетронутыми + params: [] diff --git a/generated/prod/resources_yaml/12_s3.yaml b/generated/prod/resources_yaml/12_s3.yaml new file mode 100644 index 0000000..6b7e343 --- /dev/null +++ b/generated/prod/resources_yaml/12_s3.yaml @@ -0,0 +1,121 @@ +name: s3 +service_id: 12 +service_display_name: S3 Object Storage +service_short_name: s3 +service_man: Руководство доступно по ссылке:
https://docs.s3.msk-1.ngcloud.ru/s3/s3.description.html +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 1 + kind: instance + action: create + params: + - id: 163 + code: resourceRealm + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: кластер S3 для подключения + sort: 20 + - id: 320 + code: displayName + data_type: string + required: true + descr: Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента + sort: 10 + - name: create_sub_user + id: 6 + kind: subresource + action: create + subresource: sub_user + params: + - id: 60 + code: subUserName + data_type: string + required: true + - id: 61 + code: subUserAccess + data_type: string + required: true + value_list: + - read + - write + - readwrite + - full + - name: delete + id: 5 + kind: instance + action: delete + params: [] + - name: delete_sub_user + id: 7 + kind: subresource + action: delete + subresource: sub_user + params: + - id: 63 + code: subUserName + data_type: string + required: true + descr: subUserName. По идее услуга уже создана - должен передаваться из ЛК + - name: modify + id: 4 + kind: instance + action: modify + man: При первичном вызове операции услуга Trial прекращает действие + params: + - id: 50 + code: maxSizeGbPerUser + data_type: integer + required: false + maxvalue: 50000 + minvalue: -1 + descr: maxSizeGb. Передаём руками + man: maxSizeGb. Передаём руками + - id: 51 + code: maxObjectsPerBucket + data_type: integer > 0 + required: false + maxvalue: 1e+07 + minvalue: 1 + descr: Квота на бакет. Кол-во объектов. Передаём руками + man: Квота на бакет. Кол-во объектов. Передаём руками + - id: 52 + code: maxBucketsPerUser + data_type: integer > 0 + required: false + maxvalue: 1000 + minvalue: 1 + descr: Квота на пользователя. Количество бакетов пользователя. Передаём руками + man: Квота на пользователя. Количество бакетов пользователя. Передаём руками + - name: resume + id: 3 + kind: instance + action: resume + params: [] + - name: suspend + id: 2 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/13_s3bucket.yaml b/generated/prod/resources_yaml/13_s3bucket.yaml new file mode 100644 index 0000000..68e506c --- /dev/null +++ b/generated/prod/resources_yaml/13_s3bucket.yaml @@ -0,0 +1,90 @@ +name: s3bucket +service_id: 13 +service_display_name: S3 бакет +service_short_name: s3bucket +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 51 + kind: instance + action: create + params: + - id: 124 + code: s3UserUid + data_type: uuid + required: true + ref_svc_id: 12 + descr: UID Корневой услуги S3. На основе него будет создан бакет. + - id: 125 + code: bucketName + data_type: string + required: true + descr: Имя бакета s3 + - id: 126 + code: maxSize + required: true + default: "-1" + descr: Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes + man: Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes + - id: 127 + code: readAll + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. + - id: 128 + code: listAll + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. + - id: 129 + code: corsAll + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS + - id: 130 + code: placement + data_type: string + required: true + default: HOT + value_list: + - HOT + - COLD + descr: Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения + - name: delete + id: 52 + kind: instance + action: delete + params: [] diff --git a/generated/prod/resources_yaml/149_valo_tenant.yaml b/generated/prod/resources_yaml/149_valo_tenant.yaml new file mode 100644 index 0000000..ddb0afb --- /dev/null +++ b/generated/prod/resources_yaml/149_valo_tenant.yaml @@ -0,0 +1,98 @@ +name: valo_tenant +service_id: 149 +service_display_name: VALO Cloud +service_short_name: valoTenant +service_man: '# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 202 + kind: instance + action: create + params: + - id: 655 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + sort: 10 + - id: 656 + code: domain + data_type: string + required: true + regex: ^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$ + maxlength: 61 + minlength: 3 + descr: Будет добавлено в зону valo.ngcloud.ru + sort: 20 + - id: 657 + code: valoOptions + data_type: map-fixed + required: true + default: '{}' + sort: 40 + is_modifiable: true + - id: 658 + code: s3Storage + data_type: integer > 0 + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: Параметр указывается в `Gb` + man: Данные хранятся в отдельном экземпляре S3 + sort: 30 + is_modifiable: true + - name: delete + id: 206 + kind: instance + action: delete + params: [] + - name: modify + id: 203 + kind: instance + action: modify + params: + - id: 659 + code: valoOptions + data_type: map-fixed + required: true + sort: 10 + - id: 660 + code: s3Storage + data_type: integer > 0 + required: true + descr: Параметр указывается в `Gb` + man: Данные хранятся в отдельном экземпляре S3 + sort: 20 + - name: resume + id: 205 + kind: instance + action: resume + params: [] + - name: suspend + id: 204 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/150_k8s_sthutrval_cluster.yaml b/generated/prod/resources_yaml/150_k8s_sthutrval_cluster.yaml new file mode 100644 index 0000000..3afa81e --- /dev/null +++ b/generated/prod/resources_yaml/150_k8s_sthutrval_cluster.yaml @@ -0,0 +1,227 @@ +name: k8s_sthutrval_cluster +service_id: 150 +service_display_name: Kubernetes кластер Штурвал +service_short_name: k8sSthutrvalCluster +service_man: Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 108 + kind: instance + action: create + params: + - id: 344 + code: nsxtUid + data_type: uuid + required: true + ref_svc_id: 22 + descr: InstanceUid nsxt. Nsxt должен быть создан + sort: 10 + - id: 345 + code: clusterName + data_type: string + required: true + default: example1 + regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ + unique_scope: provider + descr: Наименование кластера Kubernetes. Имя будет использоваться в fqdn (svc.$CLUSTER_NAME) + sort: 25 + - id: 346 + code: controlPlaneSizingPolicy + data_type: string + required: true + descr: Настройка Control Plane. CPU (шт) / MEM (gb).
Наверное, должна подтягиваться из свойств ресурсной платформы + sort: 30 + depends_on: vdcUid + - id: 347 + code: controlPlaneSizingDisk + data_type: integer > 0 + required: true + default: "30" + maxvalue: 999 + minvalue: 20 + descr: Настройка Control Plane. Disk (Gb) + sort: 40 + is_modifiable: true + - id: 348 + code: controlPlaneCount + data_type: integer > 0 + required: true + default: "1" + maxvalue: 9 + minvalue: 1 + sort: 50 + is_modifiable: true + - id: 349 + code: workersSizingPolicy + data_type: string + required: true + descr: Настройка workers. CPU (шт) / MEM (gb) + sort: 60 + depends_on: vdcUid + - id: 350 + code: workersSizingDisk + data_type: integer > 0 + required: true + default: "40" + maxvalue: 999 + minvalue: 20 + descr: Настройка workers. Disk (Gb) + sort: 70 + is_modifiable: true + - id: 351 + code: workersCount + data_type: integer > 0 + required: true + default: "2" + maxvalue: 99 + minvalue: 1 + descr: Настройка workers. Кол-во нод + sort: 80 + is_modifiable: true + - id: 352 + code: appVersion + data_type: string + required: true + default: 2.12.1 + value_list: + - 2.12.1 + - 2.11.0 + descr: Версия Штурвал (не является версией Kubernetes) + sort: 90 + is_modifiable: true + - id: 353 + code: needExternalAddressAPI + data_type: boolean + required: true + default: "true" + value_list: + - "true" + descr: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false + sort: 100 + - id: 354 + code: accessIpListAPI + data_type: json + required: true + default: '[]' + descr: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем + sort: 110 + is_modifiable: true + - id: 355 + code: needExternalAddressIngress + data_type: boolean + required: true + default: "true" + value_list: + - "true" + descr: Выделяет внешний IP для доступа к Ingress + sort: 120 + - id: 356 + code: accessIpListIngress + data_type: json + required: true + default: '[]' + descr: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем + sort: 130 + is_modifiable: true + - id: 747 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + sort: 20 + - name: delete + id: 109 + kind: instance + action: delete + params: [] + - name: modify + id: 110 + kind: instance + action: modify + params: + - id: 362 + code: controlPlaneCount + data_type: integer > 0 + required: false + maxvalue: 9 + minvalue: 1 + descr: Кол-во нод Control Plane + sort: 10 + - id: 363 + code: workersCount + data_type: integer > 0 + required: false + maxvalue: 99 + minvalue: 1 + descr: Кол-во нод Workers + sort: 20 + - id: 364 + code: accessIpListAPI + data_type: json + required: false + descr: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем + sort: 30 + - id: 365 + code: accessIpListIngress + data_type: json + required: false + descr: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем + sort: 40 + - id: 563 + code: appVersion + data_type: string + required: false + value_list: + - 2.11.0 + - 2.12.1 + descr: Версия Штурвал (не является версией Kubernetes)
Понижать версию нельзя
Обновиться на несколько минорных нельзя
Невозможно перепрыгнуть не с последней минорной на мажорную
Можно обновиться на несколько патчей + - id: 637 + code: controlPlaneSizingDisk + data_type: integer > 0 + required: true + - id: 638 + code: workersSizingDisk + data_type: integer > 0 + required: true + - name: reconcile + id: 253 + kind: action + action: reconcile + params: [] + - name: resume + id: 174 + kind: instance + action: resume + params: [] + - name: suspend + id: 173 + kind: instance + action: suspend + params: [] + - name: update_secrets + id: 179 + kind: action + action: update_secrets + params: [] diff --git a/generated/prod/resources_yaml/19_vc_org.yaml b/generated/prod/resources_yaml/19_vc_org.yaml new file mode 100644 index 0000000..5efd4a7 --- /dev/null +++ b/generated/prod/resources_yaml/19_vc_org.yaml @@ -0,0 +1,85 @@ +name: vc_org +service_id: 19 +service_display_name: Организация в Cloud Director +service_short_name: vcOrg +service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 136 + kind: instance + action: create + man: '`IaaS` - Доступ в Keycloak предоставляется пользователю, который создаёт услугу
`SaaS` - Без доступа к Keycloak' + params: + - id: 418 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Облако в котором будет развёрнут тенант + man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы + sort: 10 + - id: 556 + code: organizationType + data_type: string + required: true + default: iaas + value_list: + - iaas + - saas + man: Доступ к организации предоставляется только при выборе `iaas` + sort: 20 + - name: delete + id: 137 + kind: instance + action: delete + man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта**

Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' + params: [] + - name: modify + id: 207 + kind: instance + action: modify + man: Для возможности изменения параметров предварительно необходимо создать **Виртуальный датацентр (VDC)**, потом **Сетевой шлюз периметра (Edge Gateway)**
Также при удалении ipSpaces убедитесь, что все внешние IP удалены. В случае если хоть одна привязка vIP есть, данный ipSpace удалён **не будет** + params: + - id: 662 + code: vIPConfigure + data_type: array-map-fixed + required: true + sort: 10 + - name: reconcile + id: 249 + kind: action + action: reconcile + params: [] + - name: resume + id: 144 + kind: instance + action: resume + params: [] + - name: suspend + id: 143 + kind: instance + action: suspend + man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта + params: [] diff --git a/generated/prod/resources_yaml/1_dummy.yaml b/generated/prod/resources_yaml/1_dummy.yaml new file mode 100644 index 0000000..9a410ce --- /dev/null +++ b/generated/prod/resources_yaml/1_dummy.yaml @@ -0,0 +1,203 @@ +name: dummy +service_id: 1 +service_display_name: Болванка +service_short_name: dummy +service_man: '## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
```' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 18 + kind: instance + action: create + man: Это мануал к операции CREATE сервиса Болванка + params: + - id: 198 + code: durationMs + data_type: integer >= 0 + required: true + default: "0" + descr: перед завершением подождать (sleep) столько миллисекунд + man: перед завершением подождать (sleep) столько миллисекунд + sort: 20 + is_modifiable: true + - id: 199 + code: failAtStart + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, завершить джоб, ничего не записывая в cmdb + man: '**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой' + sort: 30 + is_modifiable: true + - id: 200 + code: failInProgress + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + man: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + sort: 40 + is_modifiable: true + - id: 201 + code: whereFail + data_type: integer > 0 + required: false + default: "1" + value_list: + - "1" + - "2" + - "3" + descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + sort: 50 + is_modifiable: true + - id: 242 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование ресурсной платформы для подключения + sort: 10 + - id: 286 + code: bodymessage + data_type: string + required: false + descr: Дата, уходящая в Vault + man: Дата, уходящая в Vault + sort: 60 + is_modifiable: true + - id: 321 + code: mapExample + data_type: map + required: false + is_modifiable: true + - id: 322 + code: jsonExample + data_type: json + required: false + descr: Для тестирования интерфейса + man: Для тестирования интерфейса. Сюда можно писать корректный json + is_modifiable: true + - id: 396 + code: nestedRefExample + required: false + - id: 447 + code: yamlExample + data_type: yaml + required: false + - id: 647 + code: mapFixed + data_type: map-fixed + required: false + descr: Новый тип поля + - id: 654 + code: arrayMapFixedExample + data_type: array-map-fixed + required: false + descr: Array of map-fixed + - name: delete + id: 71 + kind: instance + action: delete + params: [] + - name: modify + id: 92 + kind: instance + action: modify + params: + - id: 287 + code: durationMs + data_type: integer >= 0 + required: true + default: "0" + descr: перед завершением подождать (sleep) столько миллисекунд + man: перед завершением подождать (sleep) столько миллисекунд + sort: 10 + - id: 288 + code: failAtStart + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, завершить джоб, ничего не записывая в cmdb + sort: 20 + - id: 289 + code: failInProgress + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + sort: 30 + - id: 290 + code: whereFail + data_type: integer > 0 + required: false + value_list: + - "1" + - "2" + - "3" + descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write + sort: 40 + - id: 291 + code: bodymessage + required: false + descr: Дата, уходящая в Vault + man: Дата, уходящая в Vault + sort: 50 + - id: 373 + code: mapExample + data_type: map + required: false + default: '{}' + - id: 374 + code: jsonExample + required: false + - name: redeploy + id: 240 + kind: action + action: redeploy + params: [] + - name: resume + id: 94 + kind: instance + action: resume + params: [] + - name: suspend + id: 93 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/20_vc_org_saas.yaml b/generated/prod/resources_yaml/20_vc_org_saas.yaml similarity index 100% rename from provider/resources_yaml/20_vc_org_saas.yaml rename to generated/prod/resources_yaml/20_vc_org_saas.yaml diff --git a/generated/prod/resources_yaml/21_vc_vdc.yaml b/generated/prod/resources_yaml/21_vc_vdc.yaml new file mode 100644 index 0000000..759a964 --- /dev/null +++ b/generated/prod/resources_yaml/21_vc_vdc.yaml @@ -0,0 +1,153 @@ +name: vc_vdc +service_id: 21 +service_display_name: Виртуальный датацентр (vDC) +service_short_name: vc_vdc +service_man: '# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 9 + kind: instance + action: create + params: + - id: 30 + code: organizationUid + data_type: uuid + required: true + ref_svc_id: 19 + man: Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` + sort: 10 + - id: 335 + code: providerVdc + data_type: string + required: true + man: Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
+ sort: 30 + depends_on: organizationUid,networkProvider + - id: 361 + code: storageConfig + data_type: array-map-fixed + required: true + man: Первая политика в списке указывается как "По умолчанию" + sort: 100 + is_modifiable: true + - id: 397 + code: cpuGuaranteed + data_type: integer > 0 + required: true + default: "20" + value_list: + - "20" + - "50" + - "80" + descr: '% резервирования' + man: Свойство нельзя изменить после создания + sort: 60 + - id: 398 + code: memGuaranteed + data_type: integer > 0 + required: true + default: "20" + value_list: + - "20" + - "50" + - "80" + descr: '% резервирования' + man: После создания нельзя изменить + sort: 70 + - id: 557 + code: cpuAllocated + data_type: integer > 0 + required: true + default: "10" + maxvalue: 200 + minvalue: 1 + descr: шт + sort: 80 + is_modifiable: true + - id: 558 + code: memAllocated + data_type: integer > 0 + required: true + default: "20" + maxvalue: 4096 + minvalue: 1 + descr: gb + sort: 90 + is_modifiable: true + - id: 746 + code: networkProvider + data_type: string + required: true + sort: 20 + depends_on: organizationUid + - name: delete + id: 24 + kind: instance + action: delete + man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC**

Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' + params: [] + - name: modify + id: 112 + kind: instance + action: modify + params: + - id: 560 + code: cpuAllocated + data_type: integer > 0 + required: true + maxvalue: 999 + minvalue: 1 + descr: шт + sort: 10 + - id: 561 + code: memAllocated + data_type: integer > 0 + required: true + maxvalue: 1024 + minvalue: 1 + descr: gb + sort: 20 + - id: 562 + code: storageConfig + data_type: array-map-fixed + required: true + man: Первая политика в списке указывается как "По умолчанию" + sort: 30 + - name: reconcile + id: 250 + kind: action + action: reconcile + params: [] + - name: resume + id: 172 + kind: instance + action: resume + params: [] + - name: suspend + id: 171 + kind: instance + action: suspend + man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC + params: [] diff --git a/generated/prod/resources_yaml/22_vc_nsxt.yaml b/generated/prod/resources_yaml/22_vc_nsxt.yaml new file mode 100644 index 0000000..68fc99c --- /dev/null +++ b/generated/prod/resources_yaml/22_vc_nsxt.yaml @@ -0,0 +1,136 @@ +name: vc_nsxt +service_id: 22 +service_display_name: Сетевой шлюз периметра (Edge) +service_short_name: vc_nsxt +service_man: '# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 10 + kind: instance + action: create + params: + - id: 8 + code: vdcUid + data_type: string + required: false + ref_svc_id: 21 + descr: UUID Услуги `Виртуальный датацентр (vDC)` + man: 'Необходимо, если выбран тип подключаемой VDC: `vdc`' + sort: 10 + - id: 340 + code: needEnableAVI + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включение Load Balancer + man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать + sort: 30 + is_modifiable: true + - id: 341 + code: virtualServicesCount + data_type: integer > 0 + required: false + default: "1" + maxvalue: 4 + minvalue: 1 + descr: Кол-во виртуальных сервисов, которые выделяются на AVI + man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` + sort: 50 + is_modifiable: true + - id: 621 + code: vdcType + data_type: string + required: true + default: vdc + value_list: + - vdc + - vdcGroup + descr: Тип родительской услуги + man: При выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc + sort: 0 + - id: 622 + code: vdcGroupUid + data_type: string + required: false + ref_svc_id: 29 + descr: UUID Услуги `Группа виртуальных датацентров (groupvDC)` + man: 'Необходимо, если выбран тип подключаемой VDC: `groupvdc`' + sort: 20 + - name: delete + id: 25 + kind: instance + action: delete + man: Невозможно удалить если routed-сеть в Edge связана с:
- vApp
- VM
- Kubernetes-кластер Штурвал

Запустить операцию возможно, но будет ошибка + params: [] + - name: modify + id: 111 + kind: instance + action: modify + man: Для создания SNAT Правила убедитесь, что в организации есть свободные IP
Редактировать кол-во свободных IP можно в услуге `Организация в Cloud Director` -> `modify` + params: + - id: 368 + code: needEnableAVI + data_type: boolean + required: false + value_list: + - "false" + - "true" + descr: Включение Load Balancer + man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать + sort: 10 + - id: 369 + code: virtualServicesCount + data_type: integer > 0 + required: false + maxvalue: 4 + minvalue: 1 + descr: Кол-во виртуальных сервисов, которые выделяются на AVI + man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` + sort: 30 + - id: 371 + code: needExternalAddressSNAT + data_type: boolean + required: false + value_list: + - "false" + - "true" + descr: Создать правило SNAT на подсеть `10.10.102.0/24` + man: Параметр `ip Space для VIP` станет обязательным, если указан `true` + sort: 40 + - id: 372 + code: ipSpaceName + data_type: string + required: false + descr: Имя ip Space для внешнего IP

getKeyListFromStruct(getInstanceState(getValueFromStruct(getInstanceState(getValueFromStruct(getInstanceState(), "params.vdcUid")), "params.organizationUid")), "params.vIPConfigure") + man: Необходимо указывать, если включён параметр `Выделить VIP для SNAT` + sort: 50 + - name: reconcile + id: 252 + kind: action + action: reconcile + params: [] diff --git a/provider/resources_yaml/23_vc_vm.yaml b/generated/prod/resources_yaml/23_vc_vm.yaml similarity index 100% rename from provider/resources_yaml/23_vc_vm.yaml rename to generated/prod/resources_yaml/23_vc_vm.yaml diff --git a/generated/prod/resources_yaml/25_vcexternalip.yaml b/generated/prod/resources_yaml/25_vcexternalip.yaml new file mode 100644 index 0000000..eaa29b4 --- /dev/null +++ b/generated/prod/resources_yaml/25_vcexternalip.yaml @@ -0,0 +1,162 @@ +name: vcexternalip +service_id: 25 +service_display_name: Публичные IP адреса +service_short_name: vcexternalip +service_man: '**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса.' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 56 + kind: instance + action: create + params: + - id: 140 + code: serviceUid + data_type: string + required: true + sort: 20 + is_modifiable: true + - id: 141 + code: dnatCreate + data_type: boolean + required: true + default: "true" + descr: Необходимо создать DNAT-правило. + sort: 40 + is_modifiable: true + - id: 142 + code: internalPortAccess + data_type: string + required: true + default: "5432" + descr: Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
+ sort: 60 + is_modifiable: true + - id: 143 + code: snatCreate + data_type: boolean + required: false + descr: Необходимо создать SNAT-правило.
+ sort: 50 + is_modifiable: true + - id: 144 + code: internalAddrAccess + data_type: string + required: true + descr: Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true + sort: 70 + is_modifiable: true + - id: 324 + code: fromServiceNamespace + data_type: string + required: true + descr: Namespace составной услуги (для отображения правил FW) + sort: 80 + - id: 325 + code: fromServiceCloudEdgeName + data_type: string + required: false + descr: Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 90 + - id: 326 + code: fromServiceCloudVdcName + required: false + descr: Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 100 + - id: 327 + code: fromServiceCloudOrgName + data_type: string + required: false + descr: Org составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 110 + - id: 328 + code: fromServiceCloudVmwareUrl + data_type: string + required: false + descr: Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 120 + - id: 336 + code: ipSpaceName + data_type: string + required: true + descr: Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) + sort: 30 + is_modifiable: true + - id: 488 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 200 + - id: 624 + code: fromServiceCloudEdgeScope + data_type: string + required: false + sort: 130 + - id: 626 + code: fromServiceVdcGroupName + data_type: string + required: false + sort: 140 + - name: delete + id: 57 + kind: instance + action: delete + params: [] + - name: modify + id: 197 + kind: instance + action: modify + params: + - id: 627 + code: serviceUid + data_type: string + required: true + sort: 20 + - id: 628 + code: ipSpaceName + data_type: string + required: true + sort: 30 + - id: 629 + code: dnatCreate + data_type: boolean + required: true + sort: 40 + - id: 630 + code: snatCreate + data_type: boolean + required: true + sort: 50 + - id: 631 + code: internalPortAccess + data_type: string + required: true + sort: 60 + - id: 632 + code: internalAddrAccess + data_type: string + required: true + sort: 70 diff --git a/generated/prod/resources_yaml/26_vapp.yaml b/generated/prod/resources_yaml/26_vapp.yaml new file mode 100644 index 0000000..b48cabd --- /dev/null +++ b/generated/prod/resources_yaml/26_vapp.yaml @@ -0,0 +1,73 @@ +name: vapp +service_id: 26 +service_display_name: Виртуальный каталог ВМ (vApp) +service_short_name: vapp +service_man: '# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 69 + kind: instance + action: create + params: + - id: 190 + code: nsxtUid + data_type: uuid + required: true + ref_svc_id: 22 + descr: UUID Услуги `Сетевой шлюз периметра (Edge)` + man: К какой сети может быть подключен виртуальный каталог + sort: 20 + - id: 191 + code: vappName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$ + minlength: 3 + descr: Уникальное в рамках организации + man: Имеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин) + sort: 30 + - id: 623 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + descr: UUID Услуги `Виртуальный датацентр (vDC)` + man: В каких ресурсах будет создан виртуальный каталог + sort: 10 + - name: delete + id: 70 + kind: instance + action: delete + params: [] + - name: resume + id: 209 + kind: instance + action: resume + params: [] + - name: suspend + id: 208 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/27_vc_vm_v2.yaml b/generated/prod/resources_yaml/27_vc_vm_v2.yaml similarity index 100% rename from provider/resources_yaml/27_vc_vm_v2.yaml rename to generated/prod/resources_yaml/27_vc_vm_v2.yaml diff --git a/generated/prod/resources_yaml/28_vc_vm_v3.yaml b/generated/prod/resources_yaml/28_vc_vm_v3.yaml new file mode 100644 index 0000000..559975c --- /dev/null +++ b/generated/prod/resources_yaml/28_vc_vm_v3.yaml @@ -0,0 +1,272 @@ +name: vc_vm_v3 +service_id: 28 +service_display_name: Виртуальная машина +service_short_name: vc_vm_v3 +service_man: '# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 132 + kind: instance + action: create + man: '# Документация по использованию услуги развёртывания ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\лож определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed".

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' + params: + - id: 407 + code: vappUid + data_type: uuid + required: true + ref_svc_id: 26 + sort: 10 + - id: 408 + code: vmName + data_type: string + required: true + default: web01 + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + sort: 20 + - id: 409 + code: vmCpu + data_type: integer > 0 + required: true + default: "1" + maxvalue: 64 + minvalue: 1 + descr: шт + sort: 30 + is_modifiable: true + - id: 410 + code: vmRam + data_type: integer > 0 + required: true + default: "1" + maxvalue: 256 + minvalue: 1 + descr: Gb + sort: 40 + is_modifiable: true + - id: 411 + code: vmDisk + data_type: integer > 0 + required: false + regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ + descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму + sort: 50 + is_modifiable: true + - id: 412 + code: ipSpaceName + data_type: string + required: true + sort: 60 + depends_on: vappUid + is_modifiable: true + - id: 413 + code: accessIpList + data_type: json + required: false + regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ + descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] + sort: 70 + is_modifiable: true + - id: 414 + code: imageVm + data_type: string + required: true + sort: 90 + depends_on: vappUid + - id: 415 + code: cloudInit + data_type: yaml + required: false + descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. + sort: 200 + - id: 416 + code: userLogin + required: true + default: myuser + descr: Учётка SSH + sort: 180 + - id: 417 + code: userPublicKey + data_type: string + required: true + descr: Публичная часть SSH-Ключа для пользователя + sort: 190 + - id: 448 + code: accessPortList + data_type: string + required: true + default: '["22"]' + descr: 'По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX' + sort: 80 + is_modifiable: true + - name: delete + id: 133 + kind: instance + action: delete + params: [] + - name: modify + id: 159 + kind: instance + action: modify + params: + - id: 493 + code: vmCpu + data_type: integer > 0 + required: false + maxvalue: 64 + minvalue: 1 + descr: шт + sort: 20 + - id: 494 + code: vmRam + data_type: integer > 0 + required: false + maxvalue: 256 + minvalue: 1 + descr: Gb + sort: 30 + - id: 495 + code: vmDisk + data_type: integer > 0 + required: false + regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ + descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму + sort: 40 + - id: 496 + code: ipSpaceName + data_type: string + required: false + value_list: + - no-needed + - internet-antiddos-v1 + - internet-no-antiddos-v1 + descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev + sort: 50 + - id: 497 + code: accessIpList + data_type: json + required: false + descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] + sort: 60 + - id: 498 + code: accessPortList + data_type: json + required: false + descr: Формат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS + sort: 70 + - name: redeploy + id: 164 + kind: action + action: redeploy + params: + - id: 509 + code: vmName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + sort: 10 + - id: 510 + code: vmCpu + data_type: integer > 0 + required: true + maxvalue: 64 + minvalue: 1 + descr: шт + sort: 20 + - id: 511 + code: vmRam + data_type: integer > 0 + required: true + maxvalue: 256 + minvalue: 1 + descr: Gb + sort: 30 + - id: 512 + code: vmDisk + data_type: integer > 0 + required: false + regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ + descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму + sort: 40 + - id: 513 + code: ipSpaceName + data_type: string + required: false + value_list: + - no-needed + - internet-antiddos-v1 + - internet-no-antiddos-v1 + - internet-ipv4-v1 + descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev + sort: 50 + - id: 514 + code: accessIpList + data_type: json + required: false + regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ + descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] + sort: 60 + - id: 515 + code: imageVm + data_type: string + required: true + value_list: + - RockyLinux_9-16G-cloudinit + - Ubuntu_22-20G + - Debian_13-20G + sort: 80 + - id: 516 + code: userLogin + data_type: string + required: true + descr: Учётка SSH + sort: 180 + - id: 517 + code: userPublicKey + data_type: string + required: true + descr: Публичная часть SSH-Ключа для пользователя + sort: 190 + - id: 518 + code: cloudInit + data_type: yaml + required: false + descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. + sort: 200 + - id: 520 + code: accessPortList + data_type: json + required: false + descr: По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX + sort: 70 + - name: resume + id: 135 + kind: instance + action: resume + params: [] + - name: suspend + id: 134 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/29_vc_vdc_group.yaml b/generated/prod/resources_yaml/29_vc_vdc_group.yaml new file mode 100644 index 0000000..efec3aa --- /dev/null +++ b/generated/prod/resources_yaml/29_vc_vdc_group.yaml @@ -0,0 +1,72 @@ +name: vc_vdc_group +service_id: 29 +service_display_name: Группа датацентров +service_short_name: vcVdcGroup +service_man: '# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc`' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: add_vdc + id: 192 + kind: action + action: add_vdc + man: '**Не нужно** выбирать UUID VDC, который уже был добавлен в группу' + params: + - id: 619 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + sort: 10 + - name: create + id: 191 + kind: instance + action: create + man: '**VDC Группа** нужна для объединения всех VDC в организации в одну логическую группу.
Это позволит, к примеру, создать **общую сеть** для всех VDC в организации

- Для создания группы VDC необходимо указать **инстанс** услуги VDC (`Виртуальный датацентр`)
- **Важный момент** - удалить эту VDC из группы нельзя. Если такая потребность появится, необходимо будет **ПОЛНОСТЬЮ** удалить группу VDC и уже потом создать новую' + params: + - id: 618 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + sort: 10 + - name: delete + id: 195 + kind: instance + action: delete + params: [] + - name: reconcile + id: 251 + kind: action + action: reconcile + params: [] + - name: remove_vdc + id: 193 + kind: action + action: remove_vdc + params: + - id: 620 + code: vdcName + data_type: string + required: true + sort: 10 diff --git a/generated/prod/resources_yaml/2_template.yaml b/generated/prod/resources_yaml/2_template.yaml new file mode 100644 index 0000000..d37f0bd --- /dev/null +++ b/generated/prod/resources_yaml/2_template.yaml @@ -0,0 +1,128 @@ +name: template +service_id: 2 +service_display_name: Темплейт k8s +service_short_name: template +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 74 + kind: instance + action: create + params: + - id: 202 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 100 + is_modifiable: true + - id: 203 + code: resourceMemory + data_type: integer > 0 + required: true + default: "200" + maxvalue: 8192 + minvalue: 128 + is_modifiable: true + - id: 204 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + is_modifiable: true + - id: 205 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + is_modifiable: true + - id: 206 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - id: 207 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + - name: delete + id: 75 + kind: instance + action: delete + params: [] + - name: modify + id: 76 + kind: instance + action: modify + params: + - id: 208 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + - id: 209 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + - id: 210 + code: resourceDisk + data_type: integer > 0 + required: false + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + - id: 211 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + - name: resume + id: 98 + kind: instance + action: resume + params: [] + - name: suspend + id: 97 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/32_vmpostgre.yaml b/generated/prod/resources_yaml/32_vmpostgre.yaml new file mode 100644 index 0000000..0d022fb --- /dev/null +++ b/generated/prod/resources_yaml/32_vmpostgre.yaml @@ -0,0 +1,80 @@ +name: vmpostgre +service_id: 32 +service_display_name: vc_vm_postgresql_std +service_short_name: vmpostgre +service_man: Сервис настройки PostgreSQL для виртуальной машины.
Требует наличия ВМ +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 32 + kind: instance + action: create + params: + - id: 43 + code: vdcUid + data_type: uuid + required: true + ref_svc_id: 21 + descr: InstanceUid vdc. Vdc должен быть создан
+ - id: 44 + code: nsxtUid + data_type: uuid + required: true + ref_svc_id: 22 + descr: InstanceUid nsxt. Nsxt должен быть создан
+ - id: 45 + code: vmUid + data_type: uuid + required: true + ref_svc_id: 23 + descr: InstanceUid ВМ. ВМ должна быть создана
+ - id: 46 + code: userDb + data_type: string + required: true + default: servicedb + descr: Наименование Базы данных
+ - id: 47 + code: userLogin + data_type: string + required: true + default: serviceuser + descr: Пользователь базы данных
+ - id: 48 + code: userPassword + data_type: string + required: true + default: fj29gm48fm!d374mc9 + descr: Пароль от доступа к Базе данных
+ - id: 859 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Ресурсная платформа + - name: delete + id: 33 + kind: instance + action: delete + params: [] diff --git a/generated/prod/resources_yaml/50_nextcloud.yaml b/generated/prod/resources_yaml/50_nextcloud.yaml new file mode 100644 index 0000000..b64448e --- /dev/null +++ b/generated/prod/resources_yaml/50_nextcloud.yaml @@ -0,0 +1,184 @@ +name: nextcloud +service_id: 50 +service_display_name: Nextcloud +service_short_name: nextcloud +service_man: 'Ресурсная платформа: k8s. Автоматизация: helm chart' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 16 + kind: instance + action: create + man: DEV-Ветка сборки Nextcloud + params: + - id: 21 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Наименование домена. Если будет указан FQDN, то происходят проверки DNS. Если указан поддомен, то будет добавлен в зону services.ngcloud.ru
Можно указать клиентскую DNS-Запись. Важно, чтобы запись смотрела на Ingress кластера + sort: 10 + - id: 99 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili + man: Квота ядра пода (Указывается в mili). 1 ядро = 1000 Mili + sort: 30 + is_modifiable: true + - id: 100 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 1024 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 40 + is_modifiable: true + - id: 101 + code: resourceDisk + data_type: integer > 0 + required: true + default: "2" + maxvalue: 999 + minvalue: 2 + descr: Квота диска (Указывается в GIGAbytes)
Диск не может быть меньше 2G (!) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 192 + code: documentServer + data_type: string + required: true + default: collabora + value_list: + - collabora + descr: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ + man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен + sort: 100 + - id: 470 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + value_list: + - "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов
Не может пока быть больше одного, так как нужны SC под RWMany + sort: 60 + is_modifiable: true + - id: 564 + code: psqlUid + data_type: uuid + required: true + ref_svc_id: 90 + sort: 6 + - id: 569 + code: appVersion + required: true + default: 32.0.1 + value_list: + - 32.0.1 + sort: 20 + is_modifiable: true + - id: 570 + code: redisUid + data_type: uuid + required: true + ref_svc_id: 91 + sort: 7 + - name: delete + id: 17 + kind: instance + action: delete + man: DEV-Ветка удаления Nextcloud + params: [] + - name: modify + id: 67 + kind: instance + action: modify + params: + - id: 178 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) + man: Квота ядра пода (Указывается в mili) + - id: 179 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + - id: 180 + code: resourceDisk + data_type: integer > 0 + required: false + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + - id: 547 + code: resourceInstances + data_type: integer > 0 + required: false + value_list: + - "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов.
Не может пока быть больше одного, так как нужны SC под RWMany + - id: 571 + code: appVersion + data_type: string + required: true + value_list: + - 32.0.1 + descr: Нельзя обновляться сразу через несколько версий сразу + - name: restart + id: 66 + kind: action + action: restart + params: [] + - name: resume + id: 129 + kind: instance + action: resume + params: [] + - name: suspend + id: 128 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/81_superset.yaml b/generated/prod/resources_yaml/81_superset.yaml new file mode 100644 index 0000000..e935cbe --- /dev/null +++ b/generated/prod/resources_yaml/81_superset.yaml @@ -0,0 +1,104 @@ +name: superset +service_id: 81 +service_display_name: Apache Superset +service_short_name: superset +service_man: 'Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru)' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 12 + kind: instance + action: create + man: DEV-Ветка сборки Superset + params: + - id: 14 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 20 + code: emails + data_type: string + required: true + default: testuser1 + descr: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел + man: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел + sort: 70 + - id: 96 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро + man: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро + sort: 30 + - id: 97 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 40 + - id: 98 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + - id: 109 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 227 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 60 + - name: delete + id: 13 + kind: instance + action: delete + man: DEV-Ветка удаления Superset + params: [] diff --git a/generated/prod/resources_yaml/82_harbor.yaml b/generated/prod/resources_yaml/82_harbor.yaml new file mode 100644 index 0000000..c47b0df --- /dev/null +++ b/generated/prod/resources_yaml/82_harbor.yaml @@ -0,0 +1,103 @@ +name: harbor +service_id: 82 +service_display_name: Container Registry +service_short_name: harbor +service_man: 'Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 14 + kind: instance + action: create + man: DEV-Ветка сборки Harbor + params: + - id: 16 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 20 + - id: 17 + code: emails + data_type: string + required: true + default: testuser1 + descr: Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел + man: Учётная запись, которой будут выданы админские права. Обязана находиться в Keycloak. Можно передавать несколько через пробел + sort: 70 + - id: 110 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 30 + - id: 228 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 40 + - id: 229 + code: resourceMemory + data_type: integer > 0 + required: true + default: "200" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 50 + - id: 230 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 60 + - id: 231 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: UUID экземпляра услуги S3 + man: UUID экземпляра услуги S3 + sort: 10 + - name: delete + id: 15 + kind: instance + action: delete + man: DEV-Ветка удаления Harbor + params: [] diff --git a/generated/prod/resources_yaml/88_k8s_ziti_controller.yaml b/generated/prod/resources_yaml/88_k8s_ziti_controller.yaml new file mode 100644 index 0000000..7270f20 --- /dev/null +++ b/generated/prod/resources_yaml/88_k8s_ziti_controller.yaml @@ -0,0 +1,90 @@ +name: k8s_ziti_controller +service_id: 88 +service_display_name: k8sZitiController +service_short_name: k8sZitiController +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 99 + kind: instance + action: create + params: + - id: 294 + code: resourceCPU + data_type: integer > 0 + required: true + default: "300" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + sort: 30 + - id: 295 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + sort: 40 + - id: 296 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + - id: 297 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 60 + - id: 298 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 20 + - id: 299 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - name: delete + id: 105 + kind: instance + action: delete + params: [] diff --git a/generated/prod/resources_yaml/89_flask.yaml b/generated/prod/resources_yaml/89_flask.yaml new file mode 100644 index 0000000..114e992 --- /dev/null +++ b/generated/prod/resources_yaml/89_flask.yaml @@ -0,0 +1,156 @@ +name: flask +service_id: 89 +service_display_name: Flask +service_short_name: flask +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 88 + kind: instance + action: create + params: + - id: 248 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 249 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 20 + - id: 250 + code: resourceCPU + data_type: integer > 0 + required: true + default: "300" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + sort: 30 + is_modifiable: true + - id: 251 + code: resourceMemory + data_type: integer > 0 + required: true + default: "256" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 252 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 50 + is_modifiable: true + - id: 253 + code: gitPath + data_type: string + required: true + default: https://github.com/Foxyhhd/Baldurs-Gate-test.git + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 60 + is_modifiable: true + - id: 254 + code: jsonEnv + data_type: string + required: false + default: '{ "param": "value" }' + descr: Массив переменных, будут переданы в env приложения + sort: 70 + is_modifiable: true + - id: 255 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код + sort: 80 + - name: delete + id: 89 + kind: instance + action: delete + params: [] + - name: modify + id: 146 + kind: instance + action: modify + params: + - id: 451 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + sort: 10 + - id: 452 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + sort: 20 + - id: 453 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 30 + - id: 454 + code: gitPath + required: true + descr: Путь до Git, который будет webroot для сайта + sort: 40 + - id: 455 + code: jsonEnv + data_type: json + required: true + default: '{ "param": "value" }' + descr: Массив переменных, будут переданы в env приложения + sort: 50 + - name: resume + id: 145 + kind: instance + action: resume + params: [] + - name: suspend + id: 125 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/90_postgres.yaml b/generated/prod/resources_yaml/90_postgres.yaml new file mode 100644 index 0000000..befa68d --- /dev/null +++ b/generated/prod/resources_yaml/90_postgres.yaml @@ -0,0 +1,468 @@ +name: postgres +service_id: 90 +service_display_name: PostgreSQL +service_short_name: postgres +service_man: '# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 19 + kind: instance + action: create + man: руководство + params: + - id: 23 + code: s3Uid + data_type: uuid + required: true + ref_svc_id: 12 + descr: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + man: Экземпляр (учетная запись) S3 для резервного копирования. Резервное копирование обязательно + sort: 20 + - id: 80 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + regex: ^[1357]$ + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в кластере. 1,3,5,7 + sort: 60 + is_modifiable: true + - id: 81 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 82 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + man: Квота ядра пода (milicores) + sort: 30 + is_modifiable: true + - id: 83 + code: resourceDisk + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 102 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Кластер Kubernetes, на котором будет развернут экземпляр + sort: 10 + - id: 145 + code: needExternalAddressMaster + data_type: boolean + required: false + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к master + man: Выделяет внешний (белый) IP для доступа к master + sort: 70 + is_modifiable: true + - id: 146 + code: needExternalAddressSlave + data_type: boolean + required: false + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть + man: Выделяет внешний (белый) IP для доступа к slave. Будет работать, если slave в принципе есть + sort: 90 + is_modifiable: true + - id: 265 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + default: 0 0 * * * + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + man: Не обязательный. Время запуска бекапа + sort: 150 + is_modifiable: true + - id: 266 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: false + default: "7" + maxvalue: 99 + minvalue: 1 + man: Не обязательный. Количество сохраняемых бекапов + sort: 140 + is_modifiable: true + - id: 310 + code: appVersion + data_type: string + required: true + default: "17" + value_list: + - "17" + - "16" + - "15" + - "14" + - "13" + descr: Версия PostgreSQL + sort: 160 + is_modifiable: true + - id: 311 + code: jsonParameters + data_type: json + required: true + default: '{ "log_connections": "off", "log_disconnections": "off" }' + maxlength: 2048 + descr: Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html + sort: 170 + is_modifiable: true + - id: 312 + code: enablePgPoolerMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на запись). Будет создан новый под + sort: 180 + is_modifiable: true + - id: 313 + code: enablePgPoolerSlave + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на чтение). Будет создан новый под + sort: 190 + is_modifiable: true + - id: 314 + code: allowNoSSL + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр + sort: 200 + is_modifiable: true + - id: 329 + code: autoScale + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги + sort: 300 + - id: 330 + code: autoScalePercentage + data_type: integer > 0 + required: true + default: "10" + value_list: + - "10" + - "15" + - "20" + descr: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема + sort: 310 + - id: 331 + code: autoScaleTechWindow + data_type: integer >= 0 + required: true + default: "0" + value_list: + - "0" + - "5" + - "12" + - "23" + descr: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) + sort: 320 + - id: 337 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`' + sort: 80 + is_modifiable: true + - id: 338 + code: ipSpaceNameSlave + data_type: string + required: false + descr: Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave + sort: 100 + is_modifiable: true + - id: 339 + code: autoScaleQuotaGb + data_type: string + required: true + default: "1" + descr: Квота для autoScale + sort: 330 + - name: delete + id: 20 + kind: instance + action: delete + params: + - id: 457 + code: deleteS3Bucket + data_type: boolean + required: true + default: "true" + value_list: + - "false" + - "true" + descr: Также удалить технический S3 Bucket с бэкапами? + sort: 10 + - name: modify + id: 48 + kind: instance + action: modify + params: + - id: 91 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 40 + - id: 92 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 20 + - id: 93 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + man: Квота ядра пода (milicores) + sort: 10 + - id: 94 + code: resourceDisk + data_type: integer > 0 + required: false + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 30 + - id: 147 + code: needExternalAddressMaster + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к master + man: Выделяет внешний IP для доступа к master + sort: 50 + - id: 148 + code: needExternalAddressSlave + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к slave. Действует, если slave в принципе есть + man: Выделяет внешний IP для доступа к slave. Действует, если slave в принципе есть + sort: 70 + - id: 267 + code: ext_BACKUP_SCHEDULE + data_type: string + required: false + regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ + man: Не обязательный. Время запуска бекапа + sort: 90 + - id: 268 + code: ext_BACKUP_NUM_TO_RETAIN + data_type: integer > 0 + required: false + maxvalue: 99 + minvalue: 1 + man: Не обязательный. Количество сохраняемых бекапов + sort: 100 + - id: 315 + code: appVersion + data_type: string + required: true + value_list: + - "17" + - "16" + - "15" + - "14" + - "13" + descr: Версия PostgreSQL. Нельзя опускать вниз + sort: 110 + - id: 316 + code: jsonParameters + data_type: json + required: false + maxlength: 2048 + descr: Массив переменных, более подробнее про настройку можно посмотреть https://www.postgresql.org/docs/current/runtime-config.html + sort: 120 + - id: 317 + code: enablePgPoolerMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на запись). Будет создан новый под + sort: 130 + - id: 318 + code: enablePgPoolerSlave + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает pgPooler (на чтение). Будет создан новый под + sort: 140 + - id: 319 + code: allowNoSSL + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: По умолчанию подключение обязательное (sslmode=require). Выставление в true отключит этот параметр + sort: 150 + - id: 541 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster. Пример: `internet-no-antiddos-v1`' + sort: 60 + - id: 542 + code: ipSpaceNameSlave + data_type: string + required: false + descr: Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave + sort: 80 + - name: recovery + id: 47 + kind: action + action: recovery + params: + - id: 86 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети
+ sort: 50 + - id: 87 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes)
+ man: Квота памяти пода (Указывается в Megabytes)
+ sort: 30 + - id: 88 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili)
+ man: Квота ядра пода (Указывается в mili)
+ sort: 20 + - id: 89 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 40 + - id: 131 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 10 + - name: restart + id: 54 + kind: action + action: restart + man: ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов.
Может быть полная поломка. Крайне осторожно запускать при работе + params: [] + - name: resume + id: 114 + kind: instance + action: resume + params: [] + - name: suspend + id: 115 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/91_redis.yaml b/generated/prod/resources_yaml/91_redis.yaml new file mode 100644 index 0000000..a710a58 --- /dev/null +++ b/generated/prod/resources_yaml/91_redis.yaml @@ -0,0 +1,118 @@ +name: redis +service_id: 91 +service_display_name: Redis +service_short_name: redis +service_man: 'Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 21 + kind: instance + action: create + params: + - id: 112 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 4000 + minvalue: 500 + descr: Квота ядра пода (Указывается в mili) + man: Квота ядра пода (Указывается в mili) + sort: 20 + - id: 113 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 512 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 30 + - id: 114 + code: resourceDisk + data_type: integer > 0 + required: true + default: "30" + maxvalue: 999 + minvalue: 10 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 40 + - id: 181 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети. Если выделить больше 1 ноды, будет добавлено дополнительно 3 Sentinel ноды + sort: 50 + - id: 182 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения
+ sort: 10 + - id: 183 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к master + man: Выделяет внешний IP для доступа к master + sort: 60 + - id: 184 + code: needExternalAddressSlave + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть + man: Выделяет внешний IP для доступа к slave. Будет работать если slave в целом есть + sort: 70 + - id: 548 + code: ipSpaceNameMaster + data_type: string + required: false + descr: Имя ipSpace для публикации VIP master. Не может быть пустым, если включен needExternalAddressMaster + - id: 549 + code: ipSpaceNameSlave + data_type: string + required: false + descr: Имя ipSpace для публикации VIP slave. Не может быть пустым, если включен needExternalAddressSlave + - name: delete + id: 22 + kind: instance + action: delete + params: [] diff --git a/generated/prod/resources_yaml/92_mongodb.yaml b/generated/prod/resources_yaml/92_mongodb.yaml new file mode 100644 index 0000000..2130bab --- /dev/null +++ b/generated/prod/resources_yaml/92_mongodb.yaml @@ -0,0 +1,147 @@ +name: mongodb +service_id: 92 +service_display_name: MongoDB +service_short_name: mongodb +service_man: '# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание кластера
* **delete** — удаление кластера
* **suspend** — остановка кластера
* **resume** — запуск кластера

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания кластера

Эти параметры необходимо указать при создании кластера.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Оптимально выбирать нечётное количество узлов в кластере MongoDB, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность кластера даже при отказе одной из нод.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Выделение внешнего IP для доступа к master

Флаг, определяющий необходимость выделения внешнего IP.
Допустимые значения: `true` или `false`

### Имя ipSpace для публикации внешнего IP

Указывается только если включено выделение внешнего IP.
Пример: `internet-no-antiddos-v1`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания кластера платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 37 + kind: instance + action: create + params: + - id: 487 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `2`' + sort: 10 + - id: 588 + code: resourceMemory + data_type: integer > 0 + required: true + minvalue: 1000 + descr: 'Пример: `2048`' + - id: 589 + code: resourceCPU + data_type: integer > 0 + required: true + descr: 'Пример: `1000`' + - id: 590 + code: resourceDisk + data_type: integer > 0 + required: true + descr: 'Пример: `20`' + - id: 595 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 10 + - id: 645 + code: needExternalAddressMaster + data_type: string + required: true + default: "false" + value_list: + - "false" + - "true" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`' + - id: 646 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' + - name: create_user + id: 198 + kind: subresource + action: create + subresource: user + params: + - id: 635 + code: username + data_type: string + required: true + - id: 636 + code: dbName + data_type: string + required: true + - id: 643 + code: role + data_type: string + required: true + value_list: + - dbOwner + - read + - readWrite + - name: delete + id: 190 + kind: instance + action: delete + params: [] + - name: delete_user + id: 199 + kind: subresource + action: delete + subresource: user + params: + - id: 644 + code: username + data_type: string + required: true + - name: modify_user + id: 200 + kind: subresource + action: modify + subresource: user + params: + - id: 640 + code: username + data_type: string + required: true + - id: 641 + code: dbName + data_type: string + required: true + - id: 642 + code: role + data_type: string + required: true + value_list: + - dbOwner + - read + - readWrite + - name: resume + id: 196 + kind: instance + action: resume + params: [] + - name: suspend + id: 194 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/93_rabbitmq.yaml b/generated/prod/resources_yaml/93_rabbitmq.yaml new file mode 100644 index 0000000..4fbf1cc --- /dev/null +++ b/generated/prod/resources_yaml/93_rabbitmq.yaml @@ -0,0 +1,180 @@ +name: rabbitmq +service_id: 93 +service_display_name: RabbitMQ +service_short_name: rabbitmq +service_man: '# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

---' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 38 + kind: instance + action: create + params: + - id: 116 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 4000 + minvalue: 500 + descr: 'Пример: `2000`' + sort: 20 + is_modifiable: true + - id: 117 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1124" + maxvalue: 8192 + minvalue: 512 + descr: 'Пример: `2048`' + sort: 30 + is_modifiable: true + - id: 118 + code: resourceDisk + data_type: integer > 0 + required: true + default: "30" + maxvalue: 999 + minvalue: 5 + descr: 'Пример: `20`' + sort: 40 + is_modifiable: true + - id: 119 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 10 + - id: 224 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + sort: 50 + is_modifiable: true + - id: 225 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`' + sort: 60 + is_modifiable: true + - id: 543 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Пример: `internet-no-antiddos-v1`' + is_modifiable: true + - name: delete + id: 39 + kind: instance + action: delete + params: [] + - name: modify + id: 106 + kind: instance + action: modify + params: + - id: 304 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 4000 + minvalue: 500 + descr: 'Пример: `2000`' + sort: 10 + - id: 305 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 512 + descr: 'Пример: `2048`' + sort: 20 + - id: 306 + code: resourceDisk + data_type: integer > 0 + required: true + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `20`' + sort: 10 + - id: 307 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + sort: 40 + - id: 308 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к master + sort: 50 + - id: 309 + code: needExternalAddressSlave + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к slave + sort: 60 + - id: 545 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Пример: `internet-no-antiddos-v1`' + - id: 546 + code: ipSpaceNameSlave + data_type: string + required: false + descr: 'Пример: `internet-no-antiddos-v1`' + - name: resume + id: 117 + kind: instance + action: resume + params: [] + - name: suspend + id: 116 + kind: instance + action: suspend + man: Пока не работает из за того что RabbitmqCluster нельзя занулить + params: [] diff --git a/generated/prod/resources_yaml/94_lucee.yaml b/generated/prod/resources_yaml/94_lucee.yaml new file mode 100644 index 0000000..bf953fc --- /dev/null +++ b/generated/prod/resources_yaml/94_lucee.yaml @@ -0,0 +1,217 @@ +name: lucee +service_id: 94 +service_display_name: Lucee +service_short_name: lucee +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 49 + kind: instance + action: create + params: + - id: 103 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 104 + code: gitPath + data_type: string + required: true + default: https://github.com/xahys/testlucee + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 60 + is_modifiable: true + - id: 105 + code: jsonEnv + data_type: json + required: false + default: '{ "param": "value" }' + maxlength: 2048 + descr: Здесь вы можете задать переменные среды в формате KEY=VALUE. Они будут доступны внутри вашего приложения. + sort: 70 + is_modifiable: true + - id: 106 + code: resourceCPU + data_type: integer > 0 + required: true + default: "300" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + man: Квота ядра пода (milicores) + sort: 30 + is_modifiable: true + - id: 107 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 108 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 132 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код + sort: 80 + is_modifiable: true + - id: 139 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 50 + is_modifiable: true + - id: 263 + code: appVersion + data_type: string + required: true + default: "5.4" + value_list: + - "5.4" + - "6.0" + regex: ^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$ + descr: Версия Lucee + sort: 90 + is_modifiable: true + - name: delete + id: 50 + kind: instance + action: delete + params: [] + - name: modify + id: 55 + kind: instance + action: modify + man: Если CFS оставить пустым, то при запуске джобы берётся параметр из instanceData + params: + - id: 133 + code: gitPath + data_type: string + required: false + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 50 + - id: 134 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + sort: 60 + - id: 135 + code: jsonEnv + data_type: json + required: false + maxlength: 2048 + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 40 + - id: 136 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) 1000Mili = 1 ядро + man: Квота ядра пода (Указывается в mili) + sort: 10 + - id: 137 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 20 + - id: 138 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 30 + - id: 264 + code: appVersion + data_type: string + required: true + default: "5.4" + value_list: + - "5.4" + - "6.0" + regex: ^\d+(\.\d+){1,2}(-[a-zA-Z0-9\.]+)?$ + sort: 70 + - name: redeploy + id: 53 + kind: action + action: redeploy + params: + - id: 257 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: restart + id: 163 + kind: action + action: restart + params: [] + - name: resume + id: 95 + kind: instance + action: resume + params: [] + - name: suspend + id: 96 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/95_nodejs.yaml b/generated/prod/resources_yaml/95_nodejs.yaml new file mode 100644 index 0000000..d2e7e61 --- /dev/null +++ b/generated/prod/resources_yaml/95_nodejs.yaml @@ -0,0 +1,215 @@ +name: nodejs +service_id: 95 +service_display_name: NodeJS +service_short_name: nodejs +service_man: Развертывает контейнер в k8s кластере с node:16, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 58 + kind: instance + action: create + params: + - id: 149 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Наименование поддомена, будет добавлено в nodejs.dev.nubes.ru + sort: 10 + - id: 150 + code: gitPath + data_type: string + required: true + default: https://github.com/xahys/testnode.git + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 60 + is_modifiable: true + - id: 151 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. Если оставить пустым, healtcheck'а не будет + sort: 80 + is_modifiable: true + - id: 152 + code: jsonEnv + data_type: json + required: false + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 70 + is_modifiable: true + - id: 153 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores). 1000Mili = 1 Ядро + man: Квота ядра пода (milicores). 1000Mili = 1 Ядро + sort: 30 + is_modifiable: true + - id: 154 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1024" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 155 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 50 + is_modifiable: true + - id: 156 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 20 + - id: 270 + code: appVersion + data_type: string + required: true + default: "23" + value_list: + - "18" + - "20" + - "22" + - "23" + regex: ^(\d){2}$ + descr: Версия приложения + sort: 90 + is_modifiable: true + - name: delete + id: 61 + kind: instance + action: delete + params: [] + - name: modify + id: 59 + kind: instance + action: modify + params: + - id: 157 + code: gitPath + data_type: string + required: false + descr: Путь до Git, который будет webroot для сайта + man: Путь до Git, который будет webroot для сайта + sort: 50 + - id: 158 + code: healthPath + data_type: string + required: false + descr: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код. + sort: 60 + - id: 159 + code: jsonEnv + data_type: json + required: false + maxlength: 2048 + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 40 + - id: 160 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) 1000Mili = 1 ядро + man: Квота ядра пода (Указывается в mili) 1000Mili = 1 ядро + sort: 10 + - id: 161 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 20 + - id: 162 + code: resourceInstances + data_type: integer > 0 + required: false + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 30 + - id: 271 + code: appVersion + data_type: string + required: true + default: "23" + value_list: + - "18" + - "20" + - "22" + - "23" + regex: ^(\d){2}$ + descr: Версия приложения + man: Версия приложения + sort: 70 + - name: redeploy + id: 60 + kind: action + action: redeploy + params: + - id: 258 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: resume + id: 121 + kind: instance + action: resume + params: [] + - name: suspend + id: 120 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/96_pgadmin.yaml b/generated/prod/resources_yaml/96_pgadmin.yaml new file mode 100644 index 0000000..5b1ff7a --- /dev/null +++ b/generated/prod/resources_yaml/96_pgadmin.yaml @@ -0,0 +1,158 @@ +name: pgadmin +service_id: 96 +service_display_name: pgAdmin +service_short_name: pgadmin +service_man: Инструмент с графическим интерфейсом для управления базами данных PostgreSQL +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 62 + kind: instance + action: create + params: + - id: 164 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 165 + code: resourceCPU + data_type: integer > 0 + required: true + default: "200" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores). 1000Mili = 1 Ядро + man: Квота ядра пода (milicores). 1000Mili = 1 Ядро + sort: 30 + is_modifiable: true + - id: 166 + code: resourceMemory + data_type: integer > 0 + required: true + default: "256" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + sort: 40 + is_modifiable: true + - id: 167 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + is_modifiable: true + - id: 169 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 170 + code: login + data_type: string + required: true + regex: ^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$ + maxlength: 255 + minlength: 3 + descr: Логин для подключения к pgAdmin, в формате email (login@email.net) + man: Логин для подключения к pgAdmin, в формате email (login@email.net) + sort: 60 + - id: 171 + code: password + data_type: string + required: true + descr: Пароль для подключения к pgAdmin + man: Пароль для подключения к pgAdmin + sort: 70 + is_sensitive: true + - name: delete + id: 63 + kind: instance + action: delete + params: [] + - name: modify + id: 64 + kind: instance + action: modify + params: + - id: 172 + code: resourceCPU + data_type: integer > 0 + required: false + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + - id: 173 + code: resourceMemory + data_type: integer > 0 + required: false + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + man: Квота памяти пода (MBytes) + - id: 174 + code: resourceDisk + data_type: integer > 0 + required: false + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + - name: redeploy + id: 65 + kind: action + action: redeploy + params: + - id: 277 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: resume + id: 123 + kind: instance + action: resume + params: [] + - name: suspend + id: 122 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/97_nodered.yaml b/generated/prod/resources_yaml/97_nodered.yaml new file mode 100644 index 0000000..4edaed8 --- /dev/null +++ b/generated/prod/resources_yaml/97_nodered.yaml @@ -0,0 +1,91 @@ +name: nodered +service_id: 97 +service_display_name: NodeRed +service_short_name: nodered +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 72 + kind: instance + action: create + params: + - id: 193 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 30 + - id: 194 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + sort: 40 + - id: 195 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 60 + - id: 196 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 197 + code: domain + data_type: string + required: true + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru + man: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru + sort: 10 + - id: 323 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 50 + - name: delete + id: 73 + kind: instance + action: delete + params: [] diff --git a/generated/prod/resources_yaml/98_http.yaml b/generated/prod/resources_yaml/98_http.yaml new file mode 100644 index 0000000..58a9db9 --- /dev/null +++ b/generated/prod/resources_yaml/98_http.yaml @@ -0,0 +1,150 @@ +name: http +service_id: 98 +service_display_name: Простой HTTP контейнер +service_short_name: http +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 77 + kind: instance + action: create + params: + - id: 212 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 213 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili) + man: Квота ядра пода (Указывается в mili) + sort: 30 + is_modifiable: true + - id: 214 + code: resourceMemory + data_type: integer > 0 + required: true + default: "200" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 40 + is_modifiable: true + - id: 215 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robin + sort: 50 + is_modifiable: true + - id: 216 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 217 + code: registryPath + data_type: string + required: true + default: nexus-sa.tst.nubes.ru/docker-nubes/jolt + descr: Путь до image, который будет установлен как deployment + man: Путь до image, который будет установлен как deployment + sort: 60 + - id: 241 + code: jsonEnv + data_type: json + required: false + default: '{"param": "value"}' + descr: Массив переменных, будут переданы в env приложения + man: Массив переменных, будут переданы в env приложения + sort: 70 + - name: delete + id: 78 + kind: instance + action: delete + params: [] + - name: modify + id: 81 + kind: instance + action: modify + params: + - id: 218 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 4000 + minvalue: 100 + - id: 219 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 128 + - id: 220 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + - name: redeploy + id: 79 + kind: action + action: redeploy + params: + - id: 272 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 10 + - name: resume + id: 130 + kind: instance + action: resume + params: [] + - name: suspend + id: 131 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/99_gitea.yaml b/generated/prod/resources_yaml/99_gitea.yaml new file mode 100644 index 0000000..87f9cb9 --- /dev/null +++ b/generated/prod/resources_yaml/99_gitea.yaml @@ -0,0 +1,153 @@ +name: gitea +service_id: 99 +service_display_name: Gitea +service_short_name: gitea +service_man: Развертывает сервис контроля версий Gitea
Зависит от сервиса postgresql который должен быть развернут заранее. +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 82 + kind: instance + action: create + params: + - id: 232 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 300 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 40 + is_modifiable: true + - id: 233 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 256 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 50 + is_modifiable: true + - id: 234 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 60 + is_modifiable: true + - id: 235 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 70 + is_modifiable: true + - id: 237 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 20 + - id: 238 + code: psqlUid + data_type: uuid + required: true + ref_svc_id: 90 + descr: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически + man: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически + sort: 10 + - name: delete + id: 83 + kind: instance + action: delete + params: [] + - name: modify + id: 91 + kind: instance + action: modify + params: + - id: 259 + code: resourceCPU + data_type: integer > 0 + required: true + default: "100" + maxvalue: 4000 + minvalue: 500 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 10 + - id: 260 + code: resourceMemory + data_type: integer > 0 + required: true + default: "128" + maxvalue: 8192 + minvalue: 256 + descr: Квота памяти пода (Указывается в Megabytes) + sort: 20 + - id: 261 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 30 + - id: 262 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + man: Количество узлов в сети + sort: 40 + - name: resume + id: 127 + kind: instance + action: resume + params: [] + - name: suspend + id: 126 + kind: instance + action: suspend + params: [] diff --git a/generated/prod/resources_yaml/embed.go b/generated/prod/resources_yaml/embed.go new file mode 100644 index 0000000..72e3ff2 --- /dev/null +++ b/generated/prod/resources_yaml/embed.go @@ -0,0 +1,8 @@ +package resources_yaml + +import "embed" + +// Files embeds unified service YAML specs for provider runtime lookup. +// +//go:embed *.yaml +var Files embed.FS diff --git a/generated/test/analyze_deps.py b/generated/test/analyze_deps.py new file mode 100644 index 0000000..8739d5f --- /dev/null +++ b/generated/test/analyze_deps.py @@ -0,0 +1,38 @@ +import glob, yaml, json + +yaml_dir = "/home/naeel/terra/terraform/devops/profiles/test/generated/resources_yaml" +missing_ids = {100,110,113,114,115,116,117,119,120,149,1,20,23,27,29,2,32,50,81,82,88,89,91,92,96,97,98,99} + +results = [] +for f in sorted(glob.glob(yaml_dir + "/*.yaml")): + data = yaml.safe_load(open(f)) + sid = data.get("service_id") + if sid not in missing_ids: + continue + name = data.get("name","") + disp = data.get("service_display_name","") + ops = data.get("operations", []) + ref_params = [] + req_params = [] + for op in ops: + if op.get("kind") == "instance" and op.get("action") == "create": + for p in op.get("params", []): + code = p.get("code","") + req = p.get("required", False) + ref_svc = p.get("ref_service_id","") + if ref_svc: + ref_params.append(code + "(svc=" + str(ref_svc) + ")") + elif req: + req_params.append(code) + results.append({ + "serviceId": sid, + "name": name, + "display": disp, + "deps": ref_params, + "required_params": req_params, + }) + +for r in results: + dep_str = str(r["deps"]) if r["deps"] else "none" + req_str = str(r["required_params"]) if r["required_params"] else "[]" + print("[" + str(r["serviceId"]).rjust(3) + "] " + r["name"].ljust(25) + " deps=" + dep_str + " required=" + req_str) diff --git a/generated/test/collect_service_params.py b/generated/test/collect_service_params.py new file mode 100644 index 0000000..746ce6b --- /dev/null +++ b/generated/test/collect_service_params.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 +""" +Универсальный сборщик state_params для сервисов тест-стенда. +Создаёт инстанс, снимает параметры, суспендит. + +Использование: + python3 collect_service_params.py [display_name] +""" + +import json, sys, time, urllib.request, urllib.error, urllib.parse, os, yaml, html + +API = "https://deck-api-test.ngcloud.ru/api/v1/index.cfm" +TOKEN = open("/home/naeel/terra/terraform/secrets/test.token").read().strip() +OUT_DIR = "/home/naeel/terra/terraform/devops/profiles/test/generated/output_inventory" + +HEADERS = { + "Authorization": f"Bearer {TOKEN}", + "Content-Type": "application/json", +} + +# Минимальные значения по умолчанию для стандартных параметров +PARAM_DEFAULTS = { + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceCPU": "1000", + "resourceMemory": "1024", + "resourceDisk": "10", + "resourceInstances": "1", + "resourceInstancesShards": "1", + "needExternalAddressMaster": "false", + "needExternalAddressSlave": "false", + "needCreateKeeper": "false", + "autoScale": "false", + "durationMs": "10000", + "failAtStart": "false", + "failInProgress": "false", + "domain": "web-test-stand", + "appVersion": "latest", + "jsonEnv": "{}", + "login": "admin@ngcloud.ru", + "password": "Admin123!", + "gitPath": "https://github.com/Foxyhhd/Baldurs-Gate-test.git", + # Для dummy + "mapExample": "{}", + "jsonExample": "{}", + "yamlExample": "", + "bodymessage": "", + "whereFail": "1", +} + +# Переопределения для конкретных сервисов {service_id: {code: value}} +SVC_PARAM_OVERRIDES = { + 97: {"domain": "inv-nodered"}, + 110: {"zoneEmail": "admin@ngcloud.ru", "zoneName": "inv-test.ngcloud.ru"}, + 117: {"domain": "inv-nifi"}, + 99: {"domain": "inv-gitea"}, + 82: {"domain": "inv-harbor"}, +} + +def req(method, path, body=None): + url = API + path + data = json.dumps(body).encode() if body is not None else None + r = urllib.request.Request(url, data=data, headers=HEADERS, method=method) + try: + with urllib.request.urlopen(r, timeout=30) as resp: + raw = resp.read() + loc = resp.headers.get("Location", "") + return json.loads(raw) if raw else {}, loc + except urllib.error.HTTPError as e: + raw = e.read() + print(f" HTTP {e.code} {method} {path}: {raw[:300]}") + return None, "" + +def get(path): + result, _ = req("GET", path) + return result + +def post(path, body): + return req("POST", path, body) + +def wait_op(op_uid, timeout=300): + deadline = time.time() + timeout + while time.time() < deadline: + data = get(f"/instanceOperations/{op_uid}?fields=dtFinish,isSuccessful,errorLog,isInProgress,isPending") + if data is None: + return False, "request error" + op = data.get("instanceOperation", data) + in_prog = op.get("isInProgress", True) + pending = op.get("isPending", True) + if not in_prog and not pending: + success = op.get("isSuccessful", False) + err = op.get("errorLog", "") + return success, err + time.sleep(5) + return False, "timeout" + +def create_instance(service_id, display_name): + print(f"\n=== Создаём инстанс: svc={service_id}, name={display_name} ===") + + # 0. Загружаем YAML чтобы знать code для каждого param_id + import glob as _glob + yaml_dir = "/home/naeel/terra/terraform/devops/profiles/test/generated/resources_yaml" + yaml_files = _glob.glob(yaml_dir + "/*.yaml") + param_id_to_code = {} + for yf in yaml_files: + ydata = yaml.safe_load(open(yf)) + if ydata.get("service_id") != service_id: + continue + for op in ydata.get("operations", []): + for p in op.get("params", []): + pid = p.get("id") + code = p.get("code", "") + if pid and code: + param_id_to_code[pid] = code + break + print(f" YAML param id→code map: {len(param_id_to_code)} entries") + # 1. Создаём инстанс + resp, loc = post("/instances", { + "serviceId": service_id, + "displayName": display_name, + "descr": "inventory-collector", + }) + if resp is None: + return None + if isinstance(resp, dict): + instance_uid = resp.get("instanceUid") + else: + instance_uid = None + if not instance_uid and loc: + instance_uid = loc.rstrip("/").split("/")[-1] + if not instance_uid: + print(f" ERR: нет instanceUid. resp={resp}, loc={loc}") + return None + print(f" instanceUid: {instance_uid}") + + # 2. Создаём операцию create + resp, loc = post("/instanceOperations", { + "instanceUid": instance_uid, + "operation": "create", + }) + if resp is None: + return None + op = resp.get("instanceOperation", resp) if isinstance(resp, dict) else {} + op_uid = op.get("instanceOperationUid") + if not op_uid and loc: + op_uid = loc.rstrip("/").split("/")[-1] + if not op_uid: + print(f" ERR: нет opUid. resp={resp}, loc={loc}") + return None + print(f" opUid: {op_uid}") + + # 3. Получаем cfsParams с ID + op_detail = get(f"/instanceOperations/{op_uid}?fields=cfsParams") + if op_detail is None: + return None + cfs_params = (op_detail.get("instanceOperation") or op_detail).get("cfsParams", []) + print(f" cfsParams count: {len(cfs_params)}") + for p in cfs_params: + code_display = p.get("svcOperationCfsParam") or p.get("code", "?") + print(f" id={p['svcOperationCfsParamId']} code={code_display} required={p.get('isRequired')} default={p.get('defaultValue')} paramValue={p.get('paramValue')}") + + # 4. Заполняем параметры (POST для КАЖДОГО — иначе /run ломается с GUID-ошибкой) + filled = 0 + for p in cfs_params: + param_id = p["svcOperationCfsParamId"] + # Получаем code: сначала из прямого поля API, потом из YAML-маппинга + code = p.get("svcOperationCfsParam") or param_id_to_code.get(param_id, p.get("code", "")) + current = p.get("paramValue") + default = p.get("defaultValue") + # Переопределения для конкретного сервиса имеют наивысший приоритет + overrides = SVC_PARAM_OVERRIDES.get(service_id, {}) + + # Определяем значение + # Пропускаем pre-populated невалидные значения ('""', пустые строки) + if current is not None and str(current) not in ('""', "''", ""): + value = str(current) + elif code in overrides: + value = overrides[code] + elif default is not None and str(default).strip() != "": + # HTML-unescape дефолтов (API может вернуть " и т.п.) + value = html.unescape(str(default)) + elif code in PARAM_DEFAULTS: + value = PARAM_DEFAULTS[code] + else: + value = "" # Отправляем пустую строку — сервер требует запись для каждого параметра + + resp2, loc2 = post("/instanceOperationCfsParams", { + "instanceOperationUid": op_uid, + "svcOperationCfsParamId": param_id, + "paramValue": value, + }) + if resp2 is not None and (loc2 or resp2 != {}): + filled += 1 + elif resp2 is None: + print(f" WARN: param id={param_id} code={code} не принят API (400/422), value={value!r}") + print(f" Заполнено параметров: {filled}") + + # 5. validate-cfs — GET (в HAR это GET, не POST; на test может вернуть 405 — терпим) + req("GET", f"/instanceOperations/{op_uid}/validate-cfs") + + # 6. Run + resp4, _ = post(f"/instanceOperations/{op_uid}/run", {}) + if resp4 is None: + print(" ERR: run вернул ошибку — сервис может быть сырым/нерабочим") + return instance_uid # возвращаем uid — state может быть частичным + print(" Операция запущена, ждём завершения...") + + # 7. Ждём + success, err = wait_op(op_uid, timeout=600) + if not success: + print(f" WARN: операция завершилась с ошибкой (сервис сырой?): {err[:200]}") + # Не прерываемся — возвращаем uid, state может содержать частичные данные + else: + print(" Создание завершено успешно!") + return instance_uid + +def collect_state(instance_uid): + detail = get(f"/instances/{instance_uid}?fields=instanceUid,displayName,serviceId,svc,state,explainedStatus") + if not detail: + return None + inst = detail.get("instance", {}) + state = inst.get("state", {}) or {} + return { + "uid": instance_uid, + "name": inst.get("displayName",""), + "serviceId": inst.get("serviceId"), + "svc_code": inst.get("svc",""), + "status": inst.get("explainedStatus",""), + "params": state.get("params",{}) or {}, + "out": state.get("out",{}) or {}, + "vault_keys": list((state.get("vault",{}) or {}).keys()), + } + +def suspend_instance(instance_uid): + print(f" Суспендим {instance_uid}...") + resp, loc = post("/instanceOperations", { + "instanceUid": instance_uid, + "operation": "suspend", + }) + if resp is None: + return + op = resp.get("instanceOperation", resp) if isinstance(resp, dict) else {} + op_uid = op.get("instanceOperationUid") + if not op_uid and loc: + op_uid = loc.rstrip("/").split("/")[-1] + if not op_uid: + return + post(f"/instanceOperations/{op_uid}/run", {}) + success, err = wait_op(op_uid, timeout=300) + if success: + print(" Suspended!") + else: + print(f" WARN suspend: {err}") + +def save_to_inventory(state_data): + svc_id = state_data["serviceId"] + safe = state_data["svc_code"].replace("/","_").replace(" ","_") + fname = os.path.join(OUT_DIR, f"svc_{svc_id}_{safe}.json") + + existing = {} + if os.path.exists(fname): + existing = json.load(open(fname)) + + instances = existing.get("instances", []) + # Не дублируем + uids = {i["uid"] for i in instances} + if state_data["uid"] not in uids: + instances.append(state_data) + + result = { + "serviceId": svc_id, + "svc_code": state_data["svc_code"], + "instances": instances, + } + with open(fname, "w") as f: + json.dump(result, f, indent=2, ensure_ascii=False) + print(f" Saved: {fname}") + + # Обновляем индекс + rebuild_index() + +def rebuild_index(): + import glob + index = [] + for f in sorted(glob.glob(os.path.join(OUT_DIR, "svc_*.json"))): + data = json.load(open(f)) + all_params = set() + all_out = set() + all_vault = set() + for item in data.get("instances", []): + all_params.update((item.get("params") or {}).keys()) + out = item.get("out") + all_out.update((out.keys() if isinstance(out, dict) else [])) + all_vault.update(item.get("vault_keys", [])) + index.append({ + "serviceId": data["serviceId"], + "svc_code": data["svc_code"], + "instance_count": len(data.get("instances", [])), + "state_params_keys": sorted(all_params), + "state_out_keys": sorted(all_out), + "vault_keys": sorted(all_vault), + }) + with open(os.path.join(OUT_DIR, "index.json"), "w") as f: + json.dump(index, f, indent=2, ensure_ascii=False) + +# ===== MAIN ===== +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: collect_service_params.py [display_name]") + sys.exit(1) + + service_id = int(sys.argv[1]) + display_name = sys.argv[2] if len(sys.argv) > 2 else f"inventory-svc{service_id}" + + instance_uid = create_instance(service_id, display_name) + if not instance_uid: + print("FAILED: не удалось создать инстанс") + sys.exit(1) + + # Небольшая пауза перед чтением state + time.sleep(3) + state = collect_state(instance_uid) + if state: + print(f"\n state_params keys: {list(state['params'].keys())}") + print(f" state_out keys: {list(state['out'].keys()) if isinstance(state['out'], dict) else '-'}") + print(f" vault keys: {state['vault_keys']}") + save_to_inventory(state) + else: + print("WARN: не удалось получить state") + + suspend_instance(instance_uid) + print("\nDone.") diff --git a/generated/test/docs/akhq.md b/generated/test/docs/akhq.md new file mode 100644 index 0000000..b160ca3 --- /dev/null +++ b/generated/test/docs/akhq.md @@ -0,0 +1,13 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## MAN + +
+# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group'ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
--- +
diff --git a/generated/test/docs/akhq_example.md b/generated/test/docs/akhq_example.md new file mode 100644 index 0000000..5163b84 --- /dev/null +++ b/generated/test/docs/akhq_example.md @@ -0,0 +1,50 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Copy-ready manifest (`akhq_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_akhq" "baza" { + resource_realm = "TODO" # Выбрать платформу из списка + domain = "TODO" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + kafka_uid = "TODO" # Выбрать кластер из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_memory = 512 # Пример: `1024` + resource_cpu = 500 # Пример: `1000` + resource_instances = 1 +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_akhq.baza.state_params["имя_ключа"] +nubes_akhq.baza.state_out["имя_ключа"] +nubes_akhq.baza.state_params_flat["имя_ключа"] +nubes_akhq.baza.state_out_flat["имя_ключа"] +nubes_akhq.baza.vault_secrets["имя_ключа"] +nubes_akhq.baza.vault_url +nubes_akhq.baza.vault_user_path +nubes_akhq.baza.vault_fields +``` diff --git a/generated/test/docs/akhq_ops.md b/generated/test/docs/akhq_ops.md new file mode 100644 index 0000000..9e4364f --- /dev/null +++ b/generated/test/docs/akhq_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](akhq_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](akhq_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/test/docs/akhq_outputs.md b/generated/test/docs/akhq_outputs.md new file mode 100644 index 0000000..22405f5 --- /dev/null +++ b/generated/test/docs/akhq_outputs.md @@ -0,0 +1,33 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` + +### `vault_secrets` ключи + +- `vault_secrets["pass"]` +- `vault_secrets["user"]` + diff --git a/generated/test/docs/akhq_params.md b/generated/test/docs/akhq_params.md new file mode 100644 index 0000000..b419cf6 --- /dev/null +++ b/generated/test/docs/akhq_params.md @@ -0,0 +1,12 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Parameters + +- [Create params](akhq_params_create.md) +- [Modify params](akhq_params_modify.md) diff --git a/generated/test/docs/akhq_params_create.md b/generated/test/docs/akhq_params_create.md new file mode 100644 index 0000000..687ca12 --- /dev/null +++ b/generated/test/docs/akhq_params_create.md @@ -0,0 +1,40 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
550resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
553domainstringЕсли будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
555kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
551resource_memoryinteger512Пример: `1024`minvalue=512
552resource_cpuinteger500Пример: `1000`minvalue=500
554resource_instancesinteger1minvalue=1
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/akhq_params_modify.md b/generated/test/docs/akhq_params_modify.md new file mode 100644 index 0000000..1ba2657 --- /dev/null +++ b/generated/test/docs/akhq_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_akhq + +Service ID: `119` + +Service Name: Akhq + +[Manual](akhq.md) | [Create params](akhq_params_create.md) | [Modify params](akhq_params_modify.md) | [Output params](akhq_outputs.md) | [Operations](akhq_ops.md) | [Example](akhq_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
565kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
diff --git a/generated/test/docs/clickhouse.md b/generated/test/docs/clickhouse.md new file mode 100644 index 0000000..b496cff --- /dev/null +++ b/generated/test/docs/clickhouse.md @@ -0,0 +1,13 @@ +# Resource nubes_clickhouse + +Service ID: `120` + +Service Name: ClickHouse + +[Manual](clickhouse.md) | [Create params](clickhouse_params_create.md) | [Modify params](clickhouse_params_modify.md) | [Output params](clickhouse_outputs.md) | [Operations](clickhouse_ops.md) | [Example](clickhouse_example.md) + +## MAN + +
+# Инструкция по управлению кластерами и пользователями ClickHouse

---

## Общая информация

ClickHouse — это колоночная система управления базами данных, предназначенная для обработки аналитических запросов в реальном времени. Платформа позволяет создавать, удалять, приостанавливать и возобновлять кластеры ClickHouse, а также управлять базами данных и пользователями.

Кластер создаётся через оператор Altinity (`https://github.com/Altinity/clickhouse-operator`) в ресурсной платформе типа `k8s`

Кластеры могут создаваться шардированными с произвольным количеством реплик. Для обеспечения отказоустойчивости и координации обязательно включение параметра `Создать ClickHouse-Keeper`.

Кластера создаются с обязательным бекапом. Для этого необходимо предварительно создать услугу `S3 Object Storage`.
---

## Доступные операции

* **create** — Создание кластера
* **delete** — Удаление кластера
* **suspend** — Приостановка работы кластера
* **resume** — Возобновление работы кластера
* **create_database** — Создание базы данных
* **delete_database** — Удаление базы данных
* **create_user** — Создание пользователя и настройка прав доступа к базе данных
* **delete_user** — Удаление пользователя

## Процесс создания и первого подключения

1. Выполнить операцию `create` - будет создан кластер
2. Выполнить операцию `create_database` - будет создана база данных
3. Выполнить операцию `create_user` - будет создан пользователь (выбрать доступ подключения к БД, созданной в п.2)

## Примеры подключения

### Подключение через HTTP-интерфейс

```bash
# Проверка доступности кластера
curl "http://<username>:<password>@<host>:8123/" -d "SELECT 1"

# Выполнение запроса
curl "http://<username>:<password>@<host>:8123/" -d "SHOW DATABASES"
```

## Простые кейсы
*Создать БД и обычной таблицы*
```clickhouse
CREATE TABLE database.table
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

INSERT INTO database.table SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM database.table;
```
Базы и таблицы не реплицируются без специального ключа (`ON CLUSTER 'clickhousek8s'`)
В случае создания в стиле, описанным выше, необходимо будет выполнять действия на всех шардах/репликах


*Создание БД и шардированной таблицы*
```clickhouse
CREATE TABLE database.localUsers
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

CREATE TABLE database.shardedTable
AS localUsers
ENGINE = Distributed('clickhousek8s', 'database', 'localUsers', id);

INSERT INTO shardedTable SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM shardedTable;
```
Важный момент - в данном примере таблицы придётся также выполнять на всех нодах ClickHouse

*Создание реплицируемой и шардированной таблицы*
```clickhouse
CREATE TABLE IF NOT EXISTS database.localUsers ON cluster 'clickhousek8s'
(
user_id UInt64,
name String,
email String
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/localUsers',
'{replica}'
)
ORDER BY user_id;

CREATE TABLE IF NOT EXISTS database.users ON cluster 'clickhousek8s'
AS database.localUsers
ENGINE = Distributed('clickhousek8s', 'database', 'localUsers', rand());

INSERT INTO database.users VALUES (1, 'Alice', 'alice@example.com');
INSERT INTO database.users VALUES (2, 'Bob', 'bob@example.com');
INSERT INTO database.users VALUES (3, 'Charlie', 'charlie@example.com');
INSERT INTO database.users VALUES (4, 'David', 'david@example.com');
INSERT INTO database.users VALUES (5, 'Gosha', 'gosha@example.com');
INSERT INTO database.users VALUES (6, 'Eve', 'eve@example.com');
INSERT INTO database.users VALUES (7, 'Frank', 'frank@example.com');
INSERT INTO database.users VALUES (8, 'Grace', 'grace@example.com');
INSERT INTO database.users VALUES (9, 'Hannah', 'hannah@example.com');
INSERT INTO database.users VALUES (10, 'Ivan', 'ivan@example.com');

SELECT * FROM database.localUsers;
SELECT * FROM database.users;

SELECT COUNT(*) AS total_users FROM database.users;
SELECT COUNT(*) AS total_users_local FROM database.localUsers;
``` +
diff --git a/generated/test/docs/clickhouse_database.md b/generated/test/docs/clickhouse_database.md new file mode 100644 index 0000000..3c6c66d --- /dev/null +++ b/generated/test/docs/clickhouse_database.md @@ -0,0 +1,27 @@ +# Resource nubes_clickhouse_database + +Service: `nubes_clickhouse` (ID: 120) + +[ClickHouse (основной)](clickhouse.md) | [Operations](clickhouse_ops.md) | [Database](clickhouse_database.md) | [Example](clickhouse_database_example.md) + +## Create params + +**Обязательные параметры** + +None. + +**Параметры со значением по умолчанию** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
600db_namestringdb01Пример: `db01`regex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
602db_namestringВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ
diff --git a/generated/test/docs/clickhouse_database_example.md b/generated/test/docs/clickhouse_database_example.md new file mode 100644 index 0000000..b1a9e74 --- /dev/null +++ b/generated/test/docs/clickhouse_database_example.md @@ -0,0 +1,33 @@ +# Resource nubes_clickhouse_database — Example + +Service: `nubes_clickhouse` (ID: 120) + +[ClickHouse (основной)](clickhouse.md) | [Operations](clickhouse_ops.md) | [Database](clickhouse_database.md) | [Example](clickhouse_database_example.md) + +## Copy-ready manifest (`clickhouse_database_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_clickhouse должен быть создан заранее +# resource "nubes_clickhouse" "baza" { ... } + +resource "nubes_clickhouse_database" "example" { + clickhouse_id = nubes_clickhouse.baza.id + + # Параметры со значением по умолчанию + db_name = "db01" # Пример: `db01` +} +``` diff --git a/generated/test/docs/clickhouse_example.md b/generated/test/docs/clickhouse_example.md new file mode 100644 index 0000000..b12740e --- /dev/null +++ b/generated/test/docs/clickhouse_example.md @@ -0,0 +1,49 @@ +# Resource nubes_clickhouse + +Service ID: `120` + +Service Name: ClickHouse + +[Manual](clickhouse.md) | [Create params](clickhouse_params_create.md) | [Modify params](clickhouse_params_modify.md) | [Output params](clickhouse_outputs.md) | [Operations](clickhouse_ops.md) | [Example](clickhouse_example.md) + +## Copy-ready manifest (`clickhouse_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_clickhouse" "baza" { + startup_configuration = "TODO" + cluster_configuration = "TODO" + access_configuration = "TODO" + clickhouse_keeper_configuration = "TODO" + clickhouse_configuration = "TODO" + backup_configuration = "TODO" + autoscale_configuration = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_clickhouse.baza.state_params["имя_ключа"] +nubes_clickhouse.baza.state_out["имя_ключа"] +nubes_clickhouse.baza.state_params_flat["имя_ключа"] +nubes_clickhouse.baza.state_out_flat["имя_ключа"] +nubes_clickhouse.baza.vault_secrets["имя_ключа"] +nubes_clickhouse.baza.vault_url +nubes_clickhouse.baza.vault_user_path +nubes_clickhouse.baza.vault_fields +``` diff --git a/generated/test/docs/clickhouse_ops.md b/generated/test/docs/clickhouse_ops.md new file mode 100644 index 0000000..a2c10d2 --- /dev/null +++ b/generated/test/docs/clickhouse_ops.md @@ -0,0 +1,20 @@ +# Resource nubes_clickhouse + +Service ID: `120` + +Service Name: ClickHouse + +[Manual](clickhouse.md) | [Create params](clickhouse_params_create.md) | [Modify params](clickhouse_params_modify.md) | [Output params](clickhouse_outputs.md) | [Operations](clickhouse_ops.md) | [Example](clickhouse_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](clickhouse_params_create.md) +- `create_database` — База данных создаётся на всех репликах кластера ClickHouse. См. [nubes_clickhouse_database](clickhouse_database.md) +- `create_user` — . См. [nubes_clickhouse_user](clickhouse_user.md) +- `delete` — Удаление возможно только после остановки сервиса через операцию `suspend` После выполнения операции необходимо выждать определенное кол-во времени +- `delete_database` — Необратимое действие!!! Трижды подумай, заблокируй телефон тимлида и не выполняй это действие. См. [nubes_clickhouse_database](clickhouse_database.md) +- `delete_user` — . См. [nubes_clickhouse_user](clickhouse_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](clickhouse_params_modify.md) +- `reconcile` +- `resume` +- `suspend` — Операция приводит к удалению всех подов Persistent Volume остаются нетронутыми diff --git a/generated/test/docs/clickhouse_outputs.md b/generated/test/docs/clickhouse_outputs.md new file mode 100644 index 0000000..82ea3b7 --- /dev/null +++ b/generated/test/docs/clickhouse_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_clickhouse + +Service ID: `120` + +Service Name: ClickHouse + +[Manual](clickhouse.md) | [Create params](clickhouse_params_create.md) | [Modify params](clickhouse_params_modify.md) | [Output params](clickhouse_outputs.md) | [Operations](clickhouse_ops.md) | [Example](clickhouse_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/clickhouse_params.md b/generated/test/docs/clickhouse_params.md new file mode 100644 index 0000000..c78f96d --- /dev/null +++ b/generated/test/docs/clickhouse_params.md @@ -0,0 +1,12 @@ +# Resource nubes_clickhouse + +Service ID: `120` + +Service Name: ClickHouse + +[Manual](clickhouse.md) | [Create params](clickhouse_params_create.md) | [Modify params](clickhouse_params_modify.md) | [Output params](clickhouse_outputs.md) | [Operations](clickhouse_ops.md) | [Example](clickhouse_example.md) + +## Parameters + +- [Create params](clickhouse_params_create.md) +- [Modify params](clickhouse_params_modify.md) diff --git a/generated/test/docs/clickhouse_params_create.md b/generated/test/docs/clickhouse_params_create.md new file mode 100644 index 0000000..92fd988 --- /dev/null +++ b/generated/test/docs/clickhouse_params_create.md @@ -0,0 +1,39 @@ +# Resource nubes_clickhouse + +Service ID: `120` + +Service Name: ClickHouse + +[Manual](clickhouse.md) | [Create params](clickhouse_params_create.md) | [Modify params](clickhouse_params_modify.md) | [Output params](clickhouse_outputs.md) | [Operations](clickhouse_ops.md) | [Example](clickhouse_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
837startup_configurationmap-fixed
838cluster_configurationmap-fixed
839access_configurationmap-fixed
840clickhouse_keeper_configurationmap-fixed
841clickhouse_configurationmap-fixed
842backup_configurationmap-fixed
843autoscale_configurationmap-fixed
851clickhouse_files_configurationarray-map-fixedФормат:
- path: somethings3.xml
xmlConfig: '&lt;clickhouse&gt;......&lt;/clickhouse&gt;'
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/clickhouse_params_modify.md b/generated/test/docs/clickhouse_params_modify.md new file mode 100644 index 0000000..d53484d --- /dev/null +++ b/generated/test/docs/clickhouse_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_clickhouse + +Service ID: `120` + +Service Name: ClickHouse + +[Manual](clickhouse.md) | [Create params](clickhouse_params_create.md) | [Modify params](clickhouse_params_modify.md) | [Output params](clickhouse_outputs.md) | [Operations](clickhouse_ops.md) | [Example](clickhouse_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
844cluster_configurationmap-fixed
845clickhouse_keeper_configurationmap-fixed
846access_configurationmap-fixed
847clickhouse_configurationmap-fixed
848backup_configurationmap-fixed
849autoscale_configurationmap-fixed
850clickhouse_files_configurationarray-map-fixedФормат:
- path: somethings3.xml
xmlConfig: '&lt;clickhouse&gt;......&lt;/clickhouse&gt;'
diff --git a/generated/test/docs/clickhouse_user.md b/generated/test/docs/clickhouse_user.md new file mode 100644 index 0000000..60e4c96 --- /dev/null +++ b/generated/test/docs/clickhouse_user.md @@ -0,0 +1,29 @@ +# Resource nubes_clickhouse_user + +Service: `nubes_clickhouse` (ID: 120) + +[ClickHouse (основной)](clickhouse.md) | [Operations](clickhouse_ops.md) | [User](clickhouse_user.md) | [Example](clickhouse_user_example.md) + +## Create params + +**Обязательные параметры** + +None. + +**Параметры со значением по умолчанию** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
596usernamestringmyusernameПример: `myusername`regex=^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$; maxlength=62; minlength=2
598db_namearray-map-fixed[{}]
599access_hostsstring::/0Пример: `::/0`
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
601usernameИмя пользователя, созданное через операцию `create_user`
diff --git a/generated/test/docs/clickhouse_user_example.md b/generated/test/docs/clickhouse_user_example.md new file mode 100644 index 0000000..406e6d9 --- /dev/null +++ b/generated/test/docs/clickhouse_user_example.md @@ -0,0 +1,35 @@ +# Resource nubes_clickhouse_user — Example + +Service: `nubes_clickhouse` (ID: 120) + +[ClickHouse (основной)](clickhouse.md) | [Operations](clickhouse_ops.md) | [User](clickhouse_user.md) | [Example](clickhouse_user_example.md) + +## Copy-ready manifest (`clickhouse_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_clickhouse должен быть создан заранее +# resource "nubes_clickhouse" "baza" { ... } + +resource "nubes_clickhouse_user" "example" { + clickhouse_id = nubes_clickhouse.baza.id + + # Параметры со значением по умолчанию + username = "myusername" # Пример: `myusername` + db_name = [{}] + access_hosts = "::/0" # Пример: `::/0` +} +``` diff --git a/generated/test/docs/dnsrecord.md b/generated/test/docs/dnsrecord.md new file mode 100644 index 0000000..22f9734 --- /dev/null +++ b/generated/test/docs/dnsrecord.md @@ -0,0 +1,13 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## MAN + +
+Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи +
diff --git a/generated/test/docs/dnsrecord_example.md b/generated/test/docs/dnsrecord_example.md new file mode 100644 index 0000000..98f4f00 --- /dev/null +++ b/generated/test/docs/dnsrecord_example.md @@ -0,0 +1,49 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Copy-ready manifest (`dnsrecord_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_dnsrecord" "baza" { + zone_uid = "TODO" # UUID экземпляра услуги DNS-зоны + record_type = "A" # Тип DNS-записи + record_name = "TODO" # Имя записи. Имя зоны в конце проставляется автоматически + record_input = "TODO" # Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + record_ttl = 3600 # Time-to-Leave записи. Раз в какое время запись будет обновлена +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dnsrecord.baza.state_params["имя_ключа"] +nubes_dnsrecord.baza.state_out["имя_ключа"] +nubes_dnsrecord.baza.state_params_flat["имя_ключа"] +nubes_dnsrecord.baza.state_out_flat["имя_ключа"] +nubes_dnsrecord.baza.vault_secrets["имя_ключа"] +nubes_dnsrecord.baza.vault_url +nubes_dnsrecord.baza.vault_user_path +nubes_dnsrecord.baza.vault_fields +``` diff --git a/generated/test/docs/dnsrecord_ops.md b/generated/test/docs/dnsrecord_ops.md new file mode 100644 index 0000000..1ee6978 --- /dev/null +++ b/generated/test/docs/dnsrecord_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dnsrecord_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](dnsrecord_params_modify.md) +- `reconcile` diff --git a/generated/test/docs/dnsrecord_outputs.md b/generated/test/docs/dnsrecord_outputs.md new file mode 100644 index 0000000..4d127c2 --- /dev/null +++ b/generated/test/docs/dnsrecord_outputs.md @@ -0,0 +1,29 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dnsServers"]` +- `state_out_flat["record"]` + diff --git a/generated/test/docs/dnsrecord_params.md b/generated/test/docs/dnsrecord_params.md new file mode 100644 index 0000000..84701f9 --- /dev/null +++ b/generated/test/docs/dnsrecord_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Parameters + +- [Create params](dnsrecord_params_create.md) +- [Modify params](dnsrecord_params_modify.md) diff --git a/generated/test/docs/dnsrecord_params_create.md b/generated/test/docs/dnsrecord_params_create.md new file mode 100644 index 0000000..a4a9ec3 --- /dev/null +++ b/generated/test/docs/dnsrecord_params_create.md @@ -0,0 +1,27 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
75zone_uiduuidUUID экземпляра услуги DNS-зоныref_svc_id=109
76record_typestringТип DNS-записиvalue_list=A, AAAA, CNAME, MX, PTR, SOA, SRV, TXT, LUA, NS, SSHFP
77record_namestringИмя записи. Имя зоны в конце проставляется автоматическиunique_scope=parent
78record_inputstringЗначение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
450record_ttlinteger3600Time-to-Leave записи. Раз в какое время запись будет обновленаminvalue=30; maxvalue=604800
diff --git a/generated/test/docs/dnsrecord_params_modify.md b/generated/test/docs/dnsrecord_params_modify.md new file mode 100644 index 0000000..d3cdbdb --- /dev/null +++ b/generated/test/docs/dnsrecord_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_dnsrecord + +Service ID: `111` + +Service Name: DNS запись + +[Manual](dnsrecord.md) | [Create params](dnsrecord_params_create.md) | [Modify params](dnsrecord_params_modify.md) | [Output params](dnsrecord_outputs.md) | [Operations](dnsrecord_ops.md) | [Example](dnsrecord_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
256record_inputstringЗапись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP
diff --git a/generated/test/docs/dnszone.md b/generated/test/docs/dnszone.md new file mode 100644 index 0000000..f8249dd --- /dev/null +++ b/generated/test/docs/dnszone.md @@ -0,0 +1,13 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## MAN + +
+## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
``` +
diff --git a/generated/test/docs/dnszone_example.md b/generated/test/docs/dnszone_example.md new file mode 100644 index 0000000..41e2355 --- /dev/null +++ b/generated/test/docs/dnszone_example.md @@ -0,0 +1,52 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Copy-ready manifest (`dnszone_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_dnszone" "baza" { + zone_email = "TODO" # Контактный e-mail + zone_name = "TODO" # Имя зоны + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + zone_kind = "NATIVE" # Тип зоны + zone_time_to_refresh = 86400 # Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов + zone_time_to_retry = 7200 # Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов + zone_time_to_expire = 3600000 # Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным + zone_time_to_live = 86400 # Время, в течение которого информация будет кешироваться другими DNS-серверами +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dnszone.baza.state_params["имя_ключа"] +nubes_dnszone.baza.state_out["имя_ключа"] +nubes_dnszone.baza.state_params_flat["имя_ключа"] +nubes_dnszone.baza.state_out_flat["имя_ключа"] +nubes_dnszone.baza.vault_secrets["имя_ключа"] +nubes_dnszone.baza.vault_url +nubes_dnszone.baza.vault_user_path +nubes_dnszone.baza.vault_fields +``` diff --git a/generated/test/docs/dnszone_ops.md b/generated/test/docs/dnszone_ops.md new file mode 100644 index 0000000..e07344c --- /dev/null +++ b/generated/test/docs/dnszone_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dnszone_params_create.md) +- `delete` diff --git a/generated/test/docs/dnszone_outputs.md b/generated/test/docs/dnszone_outputs.md new file mode 100644 index 0000000..0c3556d --- /dev/null +++ b/generated/test/docs/dnszone_outputs.md @@ -0,0 +1,32 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dnsServers"]` +- `state_out_flat["dnsServers.ns1.nubes.ru"]` +- `state_out_flat["dnsServers.ns2.nubes.ru"]` +- `state_out_flat["zoneSerial"]` +- `state_out_flat["zoneSoaServer"]` + diff --git a/generated/test/docs/dnszone_params.md b/generated/test/docs/dnszone_params.md new file mode 100644 index 0000000..0f1f739 --- /dev/null +++ b/generated/test/docs/dnszone_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Parameters + +- [Create params](dnszone_params_create.md) +- [Modify params](dnszone_params_modify.md) diff --git a/generated/test/docs/dnszone_params_create.md b/generated/test/docs/dnszone_params_create.md new file mode 100644 index 0000000..0690a8f --- /dev/null +++ b/generated/test/docs/dnszone_params_create.md @@ -0,0 +1,31 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
66zone_emailstringКонтактный e-mail
67zone_namestringИмя зоныregex=^([a-z0-9-]+\.)+[a-z0-9-]+$
69zone_masters_for_slavestringИспользовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов
284resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
68zone_kindstringNATIVEТип зоныvalue_list=NATIVE, SLAVE
70zone_time_to_refreshstring86400Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов
71zone_time_to_retrystring7200Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов
72zone_time_to_expirestring3600000Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным
73zone_time_to_livestring86400Время, в течение которого информация будет кешироваться другими DNS-серверами
diff --git a/generated/test/docs/dnszone_params_modify.md b/generated/test/docs/dnszone_params_modify.md new file mode 100644 index 0000000..bd1dc13 --- /dev/null +++ b/generated/test/docs/dnszone_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_dnszone + +Service ID: `110` + +Service Name: DNS зона + +[Manual](dnszone.md) | [Create params](dnszone_params_create.md) | [Modify params](dnszone_params_modify.md) | [Output params](dnszone_outputs.md) | [Operations](dnszone_ops.md) | [Example](dnszone_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/dummy.md b/generated/test/docs/dummy.md new file mode 100644 index 0000000..53f9855 --- /dev/null +++ b/generated/test/docs/dummy.md @@ -0,0 +1,13 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## MAN + +
+## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
``` +
diff --git a/generated/test/docs/dummy_example.md b/generated/test/docs/dummy_example.md new file mode 100644 index 0000000..f384517 --- /dev/null +++ b/generated/test/docs/dummy_example.md @@ -0,0 +1,50 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Copy-ready manifest (`dummy_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_dummy" "baza" { + resource_realm = "TODO" # Платформа для развертывания + arr = "test" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + duration_ms = 0 # перед завершением подождать (sleep) столько миллисекунд + fail_at_start = false # если true, завершить джоб, ничего не записывая в cmdb + fail_in_progress = false # если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb + where_fail = 1 # принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_dummy.baza.state_params["имя_ключа"] +nubes_dummy.baza.state_out["имя_ключа"] +nubes_dummy.baza.state_params_flat["имя_ключа"] +nubes_dummy.baza.state_out_flat["имя_ключа"] +nubes_dummy.baza.vault_secrets["имя_ключа"] +nubes_dummy.baza.vault_url +nubes_dummy.baza.vault_user_path +nubes_dummy.baza.vault_fields +``` diff --git a/generated/test/docs/dummy_ops.md b/generated/test/docs/dummy_ops.md new file mode 100644 index 0000000..3af1a55 --- /dev/null +++ b/generated/test/docs/dummy_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](dummy_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](dummy_params_modify.md) +- `reconcile` +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/test/docs/dummy_outputs.md b/generated/test/docs/dummy_outputs.md new file mode 100644 index 0000000..52b99e1 --- /dev/null +++ b/generated/test/docs/dummy_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `vault_secrets` ключи + +- `vault_secrets["someKey"]` + diff --git a/generated/test/docs/dummy_params.md b/generated/test/docs/dummy_params.md new file mode 100644 index 0000000..67ae088 --- /dev/null +++ b/generated/test/docs/dummy_params.md @@ -0,0 +1,12 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Parameters + +- [Create params](dummy_params_create.md) +- [Modify params](dummy_params_modify.md) diff --git a/generated/test/docs/dummy_params_create.md b/generated/test/docs/dummy_params_create.md new file mode 100644 index 0000000..69e3367 --- /dev/null +++ b/generated/test/docs/dummy_params_create.md @@ -0,0 +1,46 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
242resource_realmstringНаименование ресурсной платформы для подключенияfunc=getAvailableResourceRealms
286bodymessagestringДата, уходящая в Vault
321map_examplemap
322json_examplejsonДля тестирования интерфейса. Сюда можно писать корректный json
396nested_ref_example
647map_fixedmap-fixedНовый тип поля
654array_map_fixed_examplearray-map-fixedArray of map-fixed
863arrarrayvalue_list=test, 2, val1
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
198duration_msinteger0перед завершением подождать (sleep) столько миллисекунд
199fail_at_startbooleanfalse**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой
value_list=false, true
200fail_in_progressbooleanfalseесли true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdbvalue_list=false, true
201where_failinteger1принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault writevalue_list=1, 2, 3
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/dummy_params_modify.md b/generated/test/docs/dummy_params_modify.md new file mode 100644 index 0000000..b601221 --- /dev/null +++ b/generated/test/docs/dummy_params_modify.md @@ -0,0 +1,21 @@ +# Resource nubes_dummy + +Service ID: `1` + +Service Name: Болванка + +[Manual](dummy.md) | [Create params](dummy_params_create.md) | [Modify params](dummy_params_modify.md) | [Output params](dummy_outputs.md) | [Operations](dummy_ops.md) | [Example](dummy_example.md) + +## Modify params + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
287duration_msintegerперед завершением подождать (sleep) столько миллисекунд
288fail_at_startbooleanесли true, завершить джоб, ничего не записывая в cmdbvalue_list=false, true
289fail_in_progressbooleanесли true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdbvalue_list=false, true
290where_failintegerпринимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault writevalue_list=1, 2, 3
291bodymessageДата, уходящая в Vault
373map_examplemap
374json_example
diff --git a/generated/test/docs/flask.md b/generated/test/docs/flask.md new file mode 100644 index 0000000..763135a --- /dev/null +++ b/generated/test/docs/flask.md @@ -0,0 +1,13 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## MAN + +
+# Инструкция по развертыванию Web-сервер с фреймворком Flask через платформу

## 1. Общая информация

Сервис **Web-сервер с фреймворком Flask** предназначен для развертывания веб-приложений на базе Flask.

### Доступные операции

create — включить сервис, развернуть веб-приложение с указанными параметрами.
delete — удалить сервис, только после suspend.
modify — изменить настройки сервиса и переразвернуть.
suspend — выключить веб-приложение, остановить обслуживание.
resume — включить веб-приложение после suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Имя домена
Описание: доменное имя для доступа к веб-приложению. Если указан FQDN, происходит проверка DNS на валидность А-записи. А-запись должна указывать на балансировщик ресурсной платформы. Если указано имя в рамках одного октета, оно будет добавлено в публичную DNS-зону.
Пример значения: строка, допустимые FQDN или имя в одном октете. Пример: example.com или myapp.

### Платформа для развертывания
Описание: кластер Kubernetes, в котором будет развернуто приложение.
Пример значения: идентификатор кластера Kubernetes.

### Квота ядра пода
Описание: количество CPU, выделяемое на под, в Milicores.
Пример значения: целое число, например 500 для 0.5 ядра.

### Квота памяти пода
Описание: количество памяти, выделяемое на под, в Megabytes.
Пример значения: целое число, например 512 для 512 MB.

### Кол-во реплик пода
Описание: количество инстансов пода для масштабирования.
Пример значения: целое число, шт, например 2.

### URL Проекта (gitpath)
Описание: полный HTTPS путь до проекта в Git-репозитории.
Пример значения: URL в формате https://github.com/myuser/myproject.git. Доступ к репозиторию должен быть публичным.

### URL HealthCheck
Описание: путь для проверки здоровья приложения внутри пода.
Пример значения: строка, путь, например /api/v1/health.

### Переменные окружения пода
Описание: структура данных, которая будет передана как переменные окружения в под.
Пример значения: ключ-значение, например {"ENV": "production"}.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Квота ядра пода: 300m (0.3 ядра)
- Квота памяти пода: 256 MB
- Кол-во реплик пода: 1

### Промышленное окружение
- Квота ядра пода: от 1000m (1 ядро) в зависимости от нагрузки
- Квота памяти пода: от 1024 MB (1 GB) с учетом потребностей приложения
- Кол-во реплик пода: от 2 для обеспечения отказоустойчивости

---

## 4. Выходные параметры
Операция create не возвращает явных выходных параметров. Успешное выполнение операции приводит к развертыванию сервиса, доступного по указанному домену.

---

## 5. Дополнительная информация
- В корне проекта должен находиться файл requirements.txt со списком зависимостей Python.
- Исходный код веб-приложения должен располагаться в директории site.
- Запуск веб-сервера осуществляется командой python app.py.
- Используется версия Python: python:3.12-slim.
- Git-репозиторий должен быть публично доступен для платформы.
- Операция delete возможна только после suspend.
- Операция modify приводит к переразвертыванию с новыми параметрами. +
diff --git a/generated/test/docs/flask_example.md b/generated/test/docs/flask_example.md new file mode 100644 index 0000000..5d15690 --- /dev/null +++ b/generated/test/docs/flask_example.md @@ -0,0 +1,49 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Copy-ready manifest (`flask_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_flask" "baza" { + startup_configuration = "TODO" + cluster_configuration = "TODO" + access_configuration = "TODO" + app_configuration = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + json_env = {} +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_flask.baza.state_params["имя_ключа"] +nubes_flask.baza.state_out["имя_ключа"] +nubes_flask.baza.state_params_flat["имя_ключа"] +nubes_flask.baza.state_out_flat["имя_ключа"] +nubes_flask.baza.vault_secrets["имя_ключа"] +nubes_flask.baza.vault_url +nubes_flask.baza.vault_user_path +nubes_flask.baza.vault_fields +``` diff --git a/generated/test/docs/flask_ops.md b/generated/test/docs/flask_ops.md new file mode 100644 index 0000000..a9ff50c --- /dev/null +++ b/generated/test/docs/flask_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](flask_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](flask_params_modify.md) +- `reconcile` +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/test/docs/flask_outputs.md b/generated/test/docs/flask_outputs.md new file mode 100644 index 0000000..3c2d51a --- /dev/null +++ b/generated/test/docs/flask_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/flask_params.md b/generated/test/docs/flask_params.md new file mode 100644 index 0000000..ca3f639 --- /dev/null +++ b/generated/test/docs/flask_params.md @@ -0,0 +1,12 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Parameters + +- [Create params](flask_params_create.md) +- [Modify params](flask_params_modify.md) diff --git a/generated/test/docs/flask_params_create.md b/generated/test/docs/flask_params_create.md new file mode 100644 index 0000000..cb3cdfb --- /dev/null +++ b/generated/test/docs/flask_params_create.md @@ -0,0 +1,39 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
1037startup_configurationmap-fixed
1038cluster_configurationmap-fixed
1039access_configurationmap-fixed
1040app_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
1041json_envmap{}
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/flask_params_modify.md b/generated/test/docs/flask_params_modify.md new file mode 100644 index 0000000..9043feb --- /dev/null +++ b/generated/test/docs/flask_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_flask + +Service ID: `89` + +Service Name: Web-сервер с фреймворком Flask + +[Manual](flask.md) | [Create params](flask_params_create.md) | [Modify params](flask_params_modify.md) | [Output params](flask_outputs.md) | [Operations](flask_ops.md) | [Example](flask_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
1042cluster_configurationmap-fixed
1043app_configurationmap-fixed
1044json_envmap
diff --git a/generated/test/docs/gitea.md b/generated/test/docs/gitea.md new file mode 100644 index 0000000..8b02101 --- /dev/null +++ b/generated/test/docs/gitea.md @@ -0,0 +1,13 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## MAN + +
+# Инструкция по развертыванию Gitea через платформу

---
## 1. Общая информация
Gitea — это лёгкий, быстрый и самодостаточный сервис хостинга Git-репозиториев с веб-интерфейсом.
Позволяет создавать и управлять репозиториями, организациями, командами, а также настраивать CI/CD через интеграции.
Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание сервиса Gitea
- **delete** — Удаление сервиса Gitea
- **modify** — Модификация сервиса Gitea
- **suspend** — Остановка сервиса Gitea
- **resume** — Запуск сервиса Gitea

---
## 2. Параметры развертывания
Ниже перечислены параметры, которые необходимо указать при создании ресурса Gitea:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развёрнут сервис). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик подов Gitea. Пример: `1`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `1024`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Размер (gigabyte) диска для базы данных:**
Размер PVC для хранения репозиториев и данных Gitea в гигабайтах. Пример: `10`

* **FQDN / Домен:**
Доменное имя, по которому будет доступен сервис. Пример: `git.example.com`

* **Инстанс услуги PostgreSQL:**
UUID услуги k8sPgsql, в которой будет создана база данных `gitea` и пользователь `techgitea`.

* **Дополнительные параметры сервера (iniServerParams):**
JSON-объект с дополнительными параметрами секции `[server]` в `app.ini`. Пример:
```json
{"MAX_CONTENT_LENGTH": "524288000"}
```

* **Дополнительные annotations для Ingress (ingressExtraAnnotations):**
JSON-объект с дополнительными annotations для Ingress-контроллера. Пример:
```json
{"nginx.ingress.kubernetes.io/proxy-body-size": "500m"}
```

---
## 3. Особенности подключения и работы с репозиториями

### 3.1. Доступ только по HTTPS
Сервис развёртывается с **rootless-образом** Gitea. SSH-сервер внутри и он **доступен только внутри кластера Kubernetes**.

**Внешний доступ к репозиториям осуществляется исключительно по HTTPS через Ingress.**

```bash
# Пример клонирования
https://<домен>/<пользователь>/<репозиторий>.git
```

### 3.2. Настройка git для передачи логина и пароля
Для работы с приватными репозиториями через HTTPS необходимо передать учётные данные. Рекомендуется использовать один из следующих способов:

**Встроить креды в URL remote**
```bash
git remote set-url origin https://<логин>:<пароль>@<домен>/<пользователь>/<репозиторий>.git
```
> **Важно:** Логин и пароль администратора создаются автоматически при развертывании и хранятся в Vault.

### 3.3. Работа с большими файлами
По умолчанию `MAX_CONTENT_LENGTH` установлен в **500 МБ**, а Ingress пропускает pack-файлы до **500 МБ**.

Если необходимо запушить файлы близкие к лимиту, увеличьте HTTP-буфер git на стороне клиента:
```bash
git config http.postBuffer 524288000
```

Для файлов **более 500 МБ** рекомендуется использовать **Git LFS**.

---
## 4. Выходные параметры
После успешного развертывания ресурса Gitea платформа возвращает ряд параметров.

Ключевым из них является:

**connectionUrl**
Это внешний URL для доступа к веб-интерфейсу и Git-репозиториям по HTTPS. Пример:
```
https://git.example.com
```

**internalConnect**
Внутренний эндпоинт для подключения к PostgreSQL из других сервисов внутри кластера.

---
## 5. Дополнительная информация
- База данных `gitea` и пользователь `techgitea` создаются автоматически в указанной услуге PostgreSQL.
- DNS-запись и TLS-сертификат (Let's Encrypt) настраиваются автоматически на основе указанного домена.
- Для изменения параметров сервера Gitea (`app.ini`) используйте операцию **modify** с полем `iniServerParams`.
- Для добавления дополнительных Ingress-annotations используйте операцию **modify** с полем `ingressExtraAnnotations`.
--- +
diff --git a/generated/test/docs/gitea_complex.md b/generated/test/docs/gitea_complex.md new file mode 100644 index 0000000..b67da5e --- /dev/null +++ b/generated/test/docs/gitea_complex.md @@ -0,0 +1,13 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## MAN + +
+Комплексная услуга из ["gittea", "postgres"] +
diff --git a/generated/test/docs/gitea_complex_example.md b/generated/test/docs/gitea_complex_example.md new file mode 100644 index 0000000..2689001 --- /dev/null +++ b/generated/test/docs/gitea_complex_example.md @@ -0,0 +1,42 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Copy-ready manifest (`gitea_complex_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_gitea_complex" "baza" { +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_gitea_complex.baza.state_params["имя_ключа"] +nubes_gitea_complex.baza.state_out["имя_ключа"] +nubes_gitea_complex.baza.state_params_flat["имя_ключа"] +nubes_gitea_complex.baza.state_out_flat["имя_ключа"] +nubes_gitea_complex.baza.vault_secrets["имя_ключа"] +nubes_gitea_complex.baza.vault_url +nubes_gitea_complex.baza.vault_user_path +nubes_gitea_complex.baza.vault_fields +``` diff --git a/generated/test/docs/gitea_complex_ops.md b/generated/test/docs/gitea_complex_ops.md new file mode 100644 index 0000000..4612ff3 --- /dev/null +++ b/generated/test/docs/gitea_complex_ops.md @@ -0,0 +1,10 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Operations + diff --git a/generated/test/docs/gitea_complex_outputs.md b/generated/test/docs/gitea_complex_outputs.md new file mode 100644 index 0000000..f0593c8 --- /dev/null +++ b/generated/test/docs/gitea_complex_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/gitea_complex_params.md b/generated/test/docs/gitea_complex_params.md new file mode 100644 index 0000000..ad58e7f --- /dev/null +++ b/generated/test/docs/gitea_complex_params.md @@ -0,0 +1,12 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Parameters + +- [Create params](gitea_complex_params_create.md) +- [Modify params](gitea_complex_params_modify.md) diff --git a/generated/test/docs/gitea_complex_params_create.md b/generated/test/docs/gitea_complex_params_create.md new file mode 100644 index 0000000..2f57dfd --- /dev/null +++ b/generated/test/docs/gitea_complex_params_create.md @@ -0,0 +1,16 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + +None. + +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/test/docs/gitea_complex_params_modify.md b/generated/test/docs/gitea_complex_params_modify.md new file mode 100644 index 0000000..472dee0 --- /dev/null +++ b/generated/test/docs/gitea_complex_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_gitea_complex + +Service ID: `114` + +Service Name: Комплексная услуга по созданию gitea + +[Manual](gitea_complex.md) | [Create params](gitea_complex_params_create.md) | [Modify params](gitea_complex_params_modify.md) | [Output params](gitea_complex_outputs.md) | [Operations](gitea_complex_ops.md) | [Example](gitea_complex_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/gitea_example.md b/generated/test/docs/gitea_example.md new file mode 100644 index 0000000..9573be7 --- /dev/null +++ b/generated/test/docs/gitea_example.md @@ -0,0 +1,50 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Copy-ready manifest (`gitea_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_gitea" "baza" { + psql_uid = "TODO" # UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_gitea.baza.state_params["имя_ключа"] +nubes_gitea.baza.state_out["имя_ключа"] +nubes_gitea.baza.state_params_flat["имя_ключа"] +nubes_gitea.baza.state_out_flat["имя_ключа"] +nubes_gitea.baza.vault_secrets["имя_ключа"] +nubes_gitea.baza.vault_url +nubes_gitea.baza.vault_user_path +nubes_gitea.baza.vault_fields +``` diff --git a/generated/test/docs/gitea_ops.md b/generated/test/docs/gitea_ops.md new file mode 100644 index 0000000..0b22aac --- /dev/null +++ b/generated/test/docs/gitea_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](gitea_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](gitea_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/test/docs/gitea_outputs.md b/generated/test/docs/gitea_outputs.md new file mode 100644 index 0000000..3944814 --- /dev/null +++ b/generated/test/docs/gitea_outputs.md @@ -0,0 +1,39 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` + diff --git a/generated/test/docs/gitea_params.md b/generated/test/docs/gitea_params.md new file mode 100644 index 0000000..f0931ff --- /dev/null +++ b/generated/test/docs/gitea_params.md @@ -0,0 +1,12 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Parameters + +- [Create params](gitea_params_create.md) +- [Modify params](gitea_params_modify.md) diff --git a/generated/test/docs/gitea_params_create.md b/generated/test/docs/gitea_params_create.md new file mode 100644 index 0000000..4f75b95 --- /dev/null +++ b/generated/test/docs/gitea_params_create.md @@ -0,0 +1,42 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
238psql_uiduuidUUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматическиref_svc_id=90
818ingress_extra_annotationsmap
821ini_server_paramsmap
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
232resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=300; maxvalue=4000
233resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=256; maxvalue=8192
234resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
235resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
237domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/gitea_params_modify.md b/generated/test/docs/gitea_params_modify.md new file mode 100644 index 0000000..d86886b --- /dev/null +++ b/generated/test/docs/gitea_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_gitea + +Service ID: `99` + +Service Name: Gitea + +[Manual](gitea.md) | [Create params](gitea_params_create.md) | [Modify params](gitea_params_modify.md) | [Output params](gitea_outputs.md) | [Operations](gitea_ops.md) | [Example](gitea_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
259resource_cpuintegerКвота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=500; maxvalue=4000
260resource_memoryintegerКвота памяти пода (Указывается в Megabytes)minvalue=256; maxvalue=8192
261resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
262resource_instancesintegerКоличество узлов в сетиminvalue=1; maxvalue=7
819ingress_extra_annotationsmapПримеры:
- `nginx.ingress.kubernetes.io/proxy-body-size: 500m` (Размер пакета)
- `nginx.ingress.kubernetes.io/proxy-read-timeout: &quot;600&quot;` (Время ожидания при чтении)
- `nginx.ingress.kubernetes.io/proxy-send-timeout: &quot;600&quot;` (Время ожидания отправке)
820ini_server_paramsmapПримеры:
- `MAX_CONTENT_LENGTH: 524288000(~500мб)` (Размер пакета в байтах)
diff --git a/generated/test/docs/harbor.md b/generated/test/docs/harbor.md new file mode 100644 index 0000000..c0cad0a --- /dev/null +++ b/generated/test/docs/harbor.md @@ -0,0 +1,13 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## MAN + +
+Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak +
diff --git a/generated/test/docs/harbor_example.md b/generated/test/docs/harbor_example.md new file mode 100644 index 0000000..57e7708 --- /dev/null +++ b/generated/test/docs/harbor_example.md @@ -0,0 +1,50 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Copy-ready manifest (`harbor_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_harbor" "baza" { + resource_realm = "TODO" # Платформа для развертывания + s3_uid = "TODO" # UUID экземпляра услуги S3 + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 100 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 200 # Квота памяти пода (Указывается в Megabytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_harbor.baza.state_params["имя_ключа"] +nubes_harbor.baza.state_out["имя_ключа"] +nubes_harbor.baza.state_params_flat["имя_ключа"] +nubes_harbor.baza.state_out_flat["имя_ключа"] +nubes_harbor.baza.vault_secrets["имя_ключа"] +nubes_harbor.baza.vault_url +nubes_harbor.baza.vault_user_path +nubes_harbor.baza.vault_fields +``` diff --git a/generated/test/docs/harbor_ops.md b/generated/test/docs/harbor_ops.md new file mode 100644 index 0000000..c97c401 --- /dev/null +++ b/generated/test/docs/harbor_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](harbor_params_create.md) +- `delete` — DEV-Ветка удаления Harbor +- `modify` — Изменение кластера. Параметры: [Modify params](harbor_params_modify.md) diff --git a/generated/test/docs/harbor_outputs.md b/generated/test/docs/harbor_outputs.md new file mode 100644 index 0000000..3422bbd --- /dev/null +++ b/generated/test/docs/harbor_outputs.md @@ -0,0 +1,40 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.k8sregistry"]` +- `state_out_flat["monitoring.k8sregistry.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` + diff --git a/generated/test/docs/harbor_params.md b/generated/test/docs/harbor_params.md new file mode 100644 index 0000000..6819254 --- /dev/null +++ b/generated/test/docs/harbor_params.md @@ -0,0 +1,12 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Parameters + +- [Create params](harbor_params_create.md) +- [Modify params](harbor_params_modify.md) diff --git a/generated/test/docs/harbor_params_create.md b/generated/test/docs/harbor_params_create.md new file mode 100644 index 0000000..f51594a --- /dev/null +++ b/generated/test/docs/harbor_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
110resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
231s3_uiduuidUUID экземпляра услуги S3ref_svc_id=12
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
16domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
228resource_cpuinteger100Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=500; maxvalue=4000
229resource_memoryinteger200Квота памяти пода (Указывается в Megabytes)minvalue=512; maxvalue=8192
230resource_instancesinteger1Количество узлов в сетиminvalue=1; maxvalue=7
diff --git a/generated/test/docs/harbor_params_modify.md b/generated/test/docs/harbor_params_modify.md new file mode 100644 index 0000000..71d42a0 --- /dev/null +++ b/generated/test/docs/harbor_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_harbor + +Service ID: `82` + +Service Name: Container Registry + +[Manual](harbor.md) | [Create params](harbor_params_create.md) | [Modify params](harbor_params_modify.md) | [Output params](harbor_outputs.md) | [Operations](harbor_ops.md) | [Example](harbor_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
822resource_instancesintegerminvalue=1; maxvalue=7
823resource_cpuintegerminvalue=500; maxvalue=4000
824resource_memoryintegerminvalue=512; maxvalue=8192
diff --git a/generated/test/docs/http.md b/generated/test/docs/http.md new file mode 100644 index 0000000..68075b0 --- /dev/null +++ b/generated/test/docs/http.md @@ -0,0 +1,13 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/http_example.md b/generated/test/docs/http_example.md new file mode 100644 index 0000000..ce0a042 --- /dev/null +++ b/generated/test/docs/http_example.md @@ -0,0 +1,51 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Copy-ready manifest (`http_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_http" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 100 # Квота ядра пода (Указывается в mili) + resource_memory = 200 # Квота памяти пода (Указывается в Megabytes) + resource_instances = 1 # Количество узлов + registry_path = "nexus-sa.tst.nubes.ru/docker-nubes/jolt" # Путь до image, который будет установлен как deployment + ingress_annotations = {} # Формат ключ/значение Поддерживает пока простые аннотации +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_http.baza.state_params["имя_ключа"] +nubes_http.baza.state_out["имя_ключа"] +nubes_http.baza.state_params_flat["имя_ключа"] +nubes_http.baza.state_out_flat["имя_ключа"] +nubes_http.baza.vault_secrets["имя_ключа"] +nubes_http.baza.vault_url +nubes_http.baza.vault_user_path +nubes_http.baza.vault_fields +``` diff --git a/generated/test/docs/http_ops.md b/generated/test/docs/http_ops.md new file mode 100644 index 0000000..fb7982b --- /dev/null +++ b/generated/test/docs/http_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](http_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](http_params_modify.md) +- `reconcile` +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/test/docs/http_outputs.md b/generated/test/docs/http_outputs.md new file mode 100644 index 0000000..9baf737 --- /dev/null +++ b/generated/test/docs/http_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/test/docs/http_params.md b/generated/test/docs/http_params.md new file mode 100644 index 0000000..07aa372 --- /dev/null +++ b/generated/test/docs/http_params.md @@ -0,0 +1,12 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Parameters + +- [Create params](http_params_create.md) +- [Modify params](http_params_modify.md) diff --git a/generated/test/docs/http_params_create.md b/generated/test/docs/http_params_create.md new file mode 100644 index 0000000..2add22b --- /dev/null +++ b/generated/test/docs/http_params_create.md @@ -0,0 +1,42 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
216resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
241json_envmapМассив переменных, будут переданы в env приложения
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
212domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
213resource_cpuinteger100Квота ядра пода (Указывается в mili)minvalue=100; maxvalue=4000
214resource_memoryinteger200Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
215resource_instancesinteger1Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robinminvalue=1; maxvalue=7
217registry_pathstringnexus-sa.tst.nubes.ru/docker-nubes/joltПуть до image, который будет установлен как deployment
782ingress_annotationsmap{}Формат ключ/значение
Поддерживает пока простые аннотации
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/http_params_modify.md b/generated/test/docs/http_params_modify.md new file mode 100644 index 0000000..75a69ec --- /dev/null +++ b/generated/test/docs/http_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_http + +Service ID: `98` + +Service Name: Простой HTTP контейнер + +[Manual](http.md) | [Create params](http_params_create.md) | [Modify params](http_params_modify.md) | [Output params](http_outputs.md) | [Operations](http_ops.md) | [Example](http_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
218resource_cpuintegerminvalue=100; maxvalue=4000
219resource_memoryintegerminvalue=128; maxvalue=8192
220resource_instancesintegerКоличество узловminvalue=1; maxvalue=7
diff --git a/generated/test/docs/index.md b/generated/test/docs/index.md new file mode 100644 index 0000000..28a360a --- /dev/null +++ b/generated/test/docs/index.md @@ -0,0 +1,47 @@ +# Ресурсы провайдера + +| ID | Ресурс | Описание | +|-----|--------|----------| +| 1 | [nubes_dummy](dummy.md) | Болванка | +| 2 | [nubes_template](template.md) | Темплейт k8s | +| 12 | [nubes_s3](s3.md) | S3 Object Storage | +| 13 | [nubes_s3bucket](s3bucket.md) | S3 бакет | +| 19 | [nubes_vc_org](vc_org.md) | Организация в Cloud Director | +| 20 | [nubes_vc_org_saas](vc_org_saas.md) | Организация [DEPRECATED] | +| 21 | [nubes_vc_vdc](vc_vdc.md) | Виртуальный датацентр (vDC) | +| 22 | [nubes_vc_nsxt](vc_nsxt.md) | Сетевой шлюз периметра (Edge) | +| 23 | [nubes_vc_vm](vc_vm.md) | VM в Cloud Director (старый формат) (vc_vm) | +| 25 | [nubes_vcexternalip](vcexternalip.md) | Публичные IP адреса | +| 26 | [nubes_vapp](vapp.md) | Виртуальный каталог ВМ (vApp) | +| 27 | [nubes_vc_vm_v2](vc_vm_v2.md) | VM в Cloud Director (vc_vmV2) | +| 28 | [nubes_vc_vm_v3](vc_vm_v3.md) | Виртуальная машина | +| 29 | [nubes_vc_vdc_group](vc_vdc_group.md) | Группа датацентров | +| 32 | [nubes_vmpostgre](vmpostgre.md) | vc_vm_postgresql_std | +| 50 | [nubes_nextcloud](nextcloud.md) | Nextcloud | +| 81 | [nubes_superset](superset.md) | Apache Superset | +| 82 | [nubes_harbor](harbor.md) | Container Registry | +| 88 | [nubes_k8s_ziti_controller](k8s_ziti_controller.md) | k8sZitiController | +| 89 | [nubes_flask](flask.md) | Web-сервер с фреймворком Flask | +| 90 | [nubes_postgres](postgres.md) | PostgreSQL | +| 91 | [nubes_redis](redis.md) | Redis | +| 92 | [nubes_mongodb](mongodb.md) | MongoDB | +| 93 | [nubes_rabbitmq](rabbitmq.md) | RabbitMQ | +| 94 | [nubes_lucee](lucee.md) | Lucee | +| 95 | [nubes_nodejs](nodejs.md) | NodeJS | +| 96 | [nubes_pgadmin](pgadmin.md) | pgAdmin | +| 97 | [nubes_nodered](nodered.md) | NodeRed | +| 98 | [nubes_http](http.md) | Простой HTTP контейнер | +| 99 | [nubes_gitea](gitea.md) | Gitea | +| 100 | [nubes_openwhisk](openwhisk.md) | Serverless Openwhisk (openwhisk_tenant) | +| 110 | [nubes_dnszone](dnszone.md) | DNS зона | +| 111 | [nubes_dnsrecord](dnsrecord.md) | DNS запись | +| 112 | [nubes_tenant](tenant.md) | Тенант в Grafana | +| 113 | [nubes_vc_complex](vc_complex.md) | Быстрый старт | +| 114 | [nubes_gitea_complex](gitea_complex.md) | Комплексная услуга по созданию gitea | +| 115 | [nubes_mariadb](mariadb.md) | Mariadb | +| 116 | [nubes_kafka](kafka.md) | ApacheKafka | +| 117 | [nubes_nifi](nifi.md) | Nifi | +| 119 | [nubes_akhq](akhq.md) | Akhq | +| 120 | [nubes_clickhouse](clickhouse.md) | ClickHouse | +| 149 | [nubes_valo_tenant](valo_tenant.md) | VALO Cloud | +| 150 | [nubes_k8s_sthutrval_cluster](k8s_sthutrval_cluster.md) | Kubernetes кластер Штурвал | diff --git a/generated/test/docs/k8s_sthutrval_cluster.md b/generated/test/docs/k8s_sthutrval_cluster.md new file mode 100644 index 0000000..b3b0523 --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster.md @@ -0,0 +1,13 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## MAN + +
+Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле +
diff --git a/generated/test/docs/k8s_sthutrval_cluster_example.md b/generated/test/docs/k8s_sthutrval_cluster_example.md new file mode 100644 index 0000000..8c8b5b2 --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_example.md @@ -0,0 +1,49 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Copy-ready manifest (`k8s_sthutrval_cluster_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_k8s_sthutrval_cluster" "baza" { + startup_configuration = "TODO" + control_plane_configuration = "TODO" + access_configuration = "TODO" + cluster_configuration = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + worker_configuration = [{}] +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_k8s_sthutrval_cluster.baza.state_params["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_out["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_params_flat["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.state_out_flat["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.vault_secrets["имя_ключа"] +nubes_k8s_sthutrval_cluster.baza.vault_url +nubes_k8s_sthutrval_cluster.baza.vault_user_path +nubes_k8s_sthutrval_cluster.baza.vault_fields +``` diff --git a/generated/test/docs/k8s_sthutrval_cluster_ops.md b/generated/test/docs/k8s_sthutrval_cluster_ops.md new file mode 100644 index 0000000..e15a212 --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_ops.md @@ -0,0 +1,18 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](k8s_sthutrval_cluster_params_create.md) +- `create_user` — . См. [nubes_k8s_sthutrval_cluster_user](k8s_sthutrval_cluster_user.md) +- `delete` +- `delete_user` — . См. [nubes_k8s_sthutrval_cluster_user](k8s_sthutrval_cluster_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](k8s_sthutrval_cluster_params_modify.md) +- `reconcile` +- `resume` +- `suspend` diff --git a/generated/test/docs/k8s_sthutrval_cluster_outputs.md b/generated/test/docs/k8s_sthutrval_cluster_outputs.md new file mode 100644 index 0000000..065be3b --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_outputs.md @@ -0,0 +1,33 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["ingressAddress"]` +- `state_out_flat["kubernetesApiAddress"]` + +### `vault_secrets` ключи + +- `vault_secrets["kubernetesAdminConfigB64"]` + diff --git a/generated/test/docs/k8s_sthutrval_cluster_params.md b/generated/test/docs/k8s_sthutrval_cluster_params.md new file mode 100644 index 0000000..641f838 --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_params.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Parameters + +- [Create params](k8s_sthutrval_cluster_params_create.md) +- [Modify params](k8s_sthutrval_cluster_params_modify.md) diff --git a/generated/test/docs/k8s_sthutrval_cluster_params_create.md b/generated/test/docs/k8s_sthutrval_cluster_params_create.md new file mode 100644 index 0000000..4ecc132 --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_params_create.md @@ -0,0 +1,39 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
750startup_configurationmap-fixed
751control_plane_configurationmap-fixed
753access_configurationmap-fixed
755cluster_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
752worker_configurationarray-map-fixed[{}]
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/k8s_sthutrval_cluster_params_modify.md b/generated/test/docs/k8s_sthutrval_cluster_params_modify.md new file mode 100644 index 0000000..f2f26fb --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_k8s_sthutrval_cluster + +Service ID: `150` + +Service Name: Kubernetes кластер Штурвал + +[Manual](k8s_sthutrval_cluster.md) | [Create params](k8s_sthutrval_cluster_params_create.md) | [Modify params](k8s_sthutrval_cluster_params_modify.md) | [Output params](k8s_sthutrval_cluster_outputs.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [Example](k8s_sthutrval_cluster_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
756cluster_configurationmap-fixed
757control_plane_configurationmap-fixed
758worker_configurationarray-map-fixed
759access_configurationmap-fixed
diff --git a/generated/test/docs/k8s_sthutrval_cluster_user.md b/generated/test/docs/k8s_sthutrval_cluster_user.md new file mode 100644 index 0000000..df2fe99 --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_user.md @@ -0,0 +1,27 @@ +# Resource nubes_k8s_sthutrval_cluster_user + +Service: `nubes_k8s_sthutrval_cluster` (ID: 150) + +[Kubernetes кластер Штурвал (основной)](k8s_sthutrval_cluster.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [User](k8s_sthutrval_cluster_user.md) | [Example](k8s_sthutrval_cluster_user_example.md) + +## Create params + +**Обязательные параметры** + +None. + +**Параметры со значением по умолчанию** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
869adminsarray-map-fixed[{}]
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
870adminsarray-map-fixed
diff --git a/generated/test/docs/k8s_sthutrval_cluster_user_example.md b/generated/test/docs/k8s_sthutrval_cluster_user_example.md new file mode 100644 index 0000000..cca35a5 --- /dev/null +++ b/generated/test/docs/k8s_sthutrval_cluster_user_example.md @@ -0,0 +1,33 @@ +# Resource nubes_k8s_sthutrval_cluster_user — Example + +Service: `nubes_k8s_sthutrval_cluster` (ID: 150) + +[Kubernetes кластер Штурвал (основной)](k8s_sthutrval_cluster.md) | [Operations](k8s_sthutrval_cluster_ops.md) | [User](k8s_sthutrval_cluster_user.md) | [Example](k8s_sthutrval_cluster_user_example.md) + +## Copy-ready manifest (`k8s_sthutrval_cluster_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_k8s_sthutrval_cluster должен быть создан заранее +# resource "nubes_k8s_sthutrval_cluster" "baza" { ... } + +resource "nubes_k8s_sthutrval_cluster_user" "example" { + k8s_sthutrval_cluster_id = nubes_k8s_sthutrval_cluster.baza.id + + # Параметры со значением по умолчанию + admins = [{}] +} +``` diff --git a/generated/test/docs/k8s_ziti_controller.md b/generated/test/docs/k8s_ziti_controller.md new file mode 100644 index 0000000..5c8b8f3 --- /dev/null +++ b/generated/test/docs/k8s_ziti_controller.md @@ -0,0 +1,13 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/k8s_ziti_controller_example.md b/generated/test/docs/k8s_ziti_controller_example.md new file mode 100644 index 0000000..e5df3c7 --- /dev/null +++ b/generated/test/docs/k8s_ziti_controller_example.md @@ -0,0 +1,50 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Copy-ready manifest (`k8s_ziti_controller_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_k8s_ziti_controller" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 300 # Квота ядра пода (milicores) + resource_memory = 512 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_k8s_ziti_controller.baza.state_params["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_out["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_params_flat["имя_ключа"] +nubes_k8s_ziti_controller.baza.state_out_flat["имя_ключа"] +nubes_k8s_ziti_controller.baza.vault_secrets["имя_ключа"] +nubes_k8s_ziti_controller.baza.vault_url +nubes_k8s_ziti_controller.baza.vault_user_path +nubes_k8s_ziti_controller.baza.vault_fields +``` diff --git a/generated/test/docs/k8s_ziti_controller_ops.md b/generated/test/docs/k8s_ziti_controller_ops.md new file mode 100644 index 0000000..8da7234 --- /dev/null +++ b/generated/test/docs/k8s_ziti_controller_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](k8s_ziti_controller_params_create.md) +- `delete` diff --git a/generated/test/docs/k8s_ziti_controller_outputs.md b/generated/test/docs/k8s_ziti_controller_outputs.md new file mode 100644 index 0000000..6fc1fdd --- /dev/null +++ b/generated/test/docs/k8s_ziti_controller_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/k8s_ziti_controller_params.md b/generated/test/docs/k8s_ziti_controller_params.md new file mode 100644 index 0000000..343d93e --- /dev/null +++ b/generated/test/docs/k8s_ziti_controller_params.md @@ -0,0 +1,12 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Parameters + +- [Create params](k8s_ziti_controller_params_create.md) +- [Modify params](k8s_ziti_controller_params_modify.md) diff --git a/generated/test/docs/k8s_ziti_controller_params_create.md b/generated/test/docs/k8s_ziti_controller_params_create.md new file mode 100644 index 0000000..dac54da --- /dev/null +++ b/generated/test/docs/k8s_ziti_controller_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
298resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
294resource_cpuinteger300Квота ядра пода (milicores)minvalue=100; maxvalue=4000
295resource_memoryinteger512Квота памяти пода (MBytes)minvalue=128; maxvalue=8192
296resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
297resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
299domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
diff --git a/generated/test/docs/k8s_ziti_controller_params_modify.md b/generated/test/docs/k8s_ziti_controller_params_modify.md new file mode 100644 index 0000000..1998e6a --- /dev/null +++ b/generated/test/docs/k8s_ziti_controller_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_k8s_ziti_controller + +Service ID: `88` + +Service Name: k8sZitiController + +[Manual](k8s_ziti_controller.md) | [Create params](k8s_ziti_controller_params_create.md) | [Modify params](k8s_ziti_controller_params_modify.md) | [Output params](k8s_ziti_controller_outputs.md) | [Operations](k8s_ziti_controller_ops.md) | [Example](k8s_ziti_controller_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/kafka.md b/generated/test/docs/kafka.md new file mode 100644 index 0000000..34f2d04 --- /dev/null +++ b/generated/test/docs/kafka.md @@ -0,0 +1,13 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## MAN + +
+# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Идентификатор организации
Уникальный идентификатор созданной организации. Используется для всех дальнейших API-операций.

### Статус организации
Отражает текущее состояние: active, suspended, modifying, deleting.

### Параметры сети
Выходные данные при наличии ipSpaces: список выделенных внешних IP, имя ip_space, количество IP.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)**;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к этой организации.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.

+
diff --git a/generated/test/docs/kafka_example.md b/generated/test/docs/kafka_example.md new file mode 100644 index 0000000..4affd5e --- /dev/null +++ b/generated/test/docs/kafka_example.md @@ -0,0 +1,50 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Copy-ready manifest (`kafka_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_kafka" "baza" { + resource_instances = 1 # Пример: `2` + resource_memory = 1 # Пример: `2048` + resource_cpu = 1 # Пример: `2000` + resource_disk = 1 # Пример: `20` + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_kafka.baza.state_params["имя_ключа"] +nubes_kafka.baza.state_out["имя_ключа"] +nubes_kafka.baza.state_params_flat["имя_ключа"] +nubes_kafka.baza.state_out_flat["имя_ключа"] +nubes_kafka.baza.vault_secrets["имя_ключа"] +nubes_kafka.baza.vault_url +nubes_kafka.baza.vault_user_path +nubes_kafka.baza.vault_fields +``` diff --git a/generated/test/docs/kafka_ops.md b/generated/test/docs/kafka_ops.md new file mode 100644 index 0000000..d21d9b4 --- /dev/null +++ b/generated/test/docs/kafka_ops.md @@ -0,0 +1,21 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](kafka_params_create.md) +- `create_topic` — # Инструкция по управлению топиками Apache Kafka через платформу ## 1. Общая информация --- Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам. Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API. --- ## Доступные операции - **create_topic** — Создание топика - **modify_topic** — Модификация топика - **delete_topic** — Удаление топика --- ## 2. Параметры создания топика Ниже перечислены параметры, которые необходимо указать при создании нового топика: * **Имя топика** Название топика. Должно быть уникальным в пределах кластера. Пример: `orders-events`. * **Партиции** Количество партиций топика. Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки. Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки. * **Реплики** Количество реплик для каждой партиции. Обычно равно количеству доступных брокеров или меньше. Пример: `3`. --- ## 4. Параметры модификации топика Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными: * **Имя топика** Название топика. Пример: `orders-events`. * **Партиции** Новое количество партиций. Пример: `12`. * **Реплики** Новое количество реплик. Пример: `2`. --- ## 5. Рекомендации * Планируйте количество партиций с запасом — уменьшение их числа невозможно. * Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров. * Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки. ---. См. [nubes_kafka_topic](kafka_topic.md) +- `create_user` — # Инструкция по управлению пользователями Apache Kafka. --- ## 1. Общая информация Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру. Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам. Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka. --- ## Доступные операции - **create_user** — Создание пользователя - **modify_user** — Модификация пользователя - **delete_user** — Удаление пользователя --- ## 2. Параметры создания пользователя Ниже перечислены параметры, которые необходимо указать при создании нового пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 3. Параметры модификации пользователя Ниже перечислены параметры, которые необходимо указать при модификации пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 4. Параметры удалении пользователя Ниже перечислены параметры, которые необходимо указать при удалении пользователя: * **Имя пользователя** Имя удаляемого пользователя. Пример: `username-admin` ## 5. Выходные параметры После успешного создания пользователя платформа возвращает параметры, необходимые для подключения: * **ca.crt** Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении. Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **ca.p12** PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.p12 ``` * **ca.password** Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно. * **user.crt** Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka. Используется совместно с приватным ключом `user.key` для аутентификации. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **user.key** Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.key ``` * **user.p12** PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo "<содержание поля>" | base64 -d > <название>.p12 ``` * **user.password** Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно. Эти параметры используются для настройки TLS-подключения. --- ## 6. Пример конфигурации клиента внутри кластера Пример пода: ```yaml apiVersion: v1 kind: Pod metadata: name: kafka-4 spec: volumes: - name: work emptyDir: {} containers: - name: kcat image: quay.io/strimzi/kafka:latest-kafka-4.0.0 command: ["sleep", "5600"] volumeMounts: - name: work mountPath: /work restartPolicy: Never ``` Создаём под и сохраняем сертификаты: ```bash kubectl cp user.p12 kafka-4:/work/ kubectl cp ca.p12 kafka-4:/work/ ``` Создаём файлы конфигурации: ```bash cat > /work/user-read.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` ```bash cat > /work/user-write.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user-write.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` Листинг доступных топиков: ```bash /opt/kafka/bin/kafka-topics.sh \ --bootstrap-server kafkak8s-bootstrap-external:9093 \ --command-config /work/user-read.properties \ --list ``` Запись сообщений: ```bash /opt/kafka/bin/kafka-console-producer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --producer.config /work/user-write.properties ``` Чтение сообщений: ```bash /opt/kafka/bin/kafka-console-consumer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --from-beginning \ --group \ --consumer.config /work/user-read.properties \ --max-messages 50 ``` --- ## 7. Пример внешнего подключения с использованием **kcat** **Листинг топиков** ```bash kcat -b :9093 \ -L \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` **Запись сообщения** ```bash echo "hello from kcat" | kcat -P \ -b :9093 \ -t write-test \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-write.crt \ -X ssl.key.location=./user-write.key \ -X ssl.endpoint.identification.algorithm=none ``` **Чтение сообщений** ```bash kcat -C \ -b :9093 \ -t write-test \ -o beginning \ -q \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` --- ## 9. Рекомендации * Минимизируйте набор разрешённых операций для повышения безопасности. * Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов. * Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**. * После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам. ---. См. [nubes_kafka_user](kafka_user.md) +- `delete` +- `delete_topic` — . См. [nubes_kafka_topic](kafka_topic.md) +- `delete_user` — # Инструкция по управлению пользователями Apache Kafka. --- ## 1. Общая информация Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру. Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам. Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka. --- ## Доступные операции - **create_user** — Создание пользователя - **modify_user** — Модификация пользователя - **delete_user** — Удаление пользователя --- ## 2. Параметры создания пользователя Ниже перечислены параметры, которые необходимо указать при создании нового пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 3. Параметры модификации пользователя Ниже перечислены параметры, которые необходимо указать при модификации пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 4. Параметры удалении пользователя Ниже перечислены параметры, которые необходимо указать при удалении пользователя: * **Имя пользователя** Имя удаляемого пользователя. Пример: `username-admin` ## 5. Выходные параметры После успешного создания пользователя платформа возвращает параметры, необходимые для подключения: * **ca.crt** Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении. Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **ca.p12** PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.p12 ``` * **ca.password** Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно. * **user.crt** Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka. Используется совместно с приватным ключом `user.key` для аутентификации. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **user.key** Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.key ``` * **user.p12** PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo "<содержание поля>" | base64 -d > <название>.p12 ``` * **user.password** Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно. Эти параметры используются для настройки TLS-подключения. --- ## 6. Пример конфигурации клиента внутри кластера Пример пода: ```yaml apiVersion: v1 kind: Pod metadata: name: kafka-4 spec: volumes: - name: work emptyDir: {} containers: - name: kcat image: quay.io/strimzi/kafka:latest-kafka-4.0.0 command: ["sleep", "5600"] volumeMounts: - name: work mountPath: /work restartPolicy: Never ``` Создаём под и сохраняем сертификаты: ```bash kubectl cp user.p12 kafka-4:/work/ kubectl cp ca.p12 kafka-4:/work/ ``` Создаём файлы конфигурации: ```bash cat > /work/user-read.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` ```bash cat > /work/user-write.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user-write.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` Листинг доступных топиков: ```bash /opt/kafka/bin/kafka-topics.sh \ --bootstrap-server kafkak8s-bootstrap-external:9093 \ --command-config /work/user-read.properties \ --list ``` Запись сообщений: ```bash /opt/kafka/bin/kafka-console-producer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --producer.config /work/user-write.properties ``` Чтение сообщений: ```bash /opt/kafka/bin/kafka-console-consumer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --from-beginning \ --group \ --consumer.config /work/user-read.properties \ --max-messages 50 ``` --- ## 7. Пример внешнего подключения с использованием **kcat** **Листинг топиков** ```bash kcat -b :9093 \ -L \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` **Запись сообщения** ```bash echo "hello from kcat" | kcat -P \ -b :9093 \ -t write-test \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-write.crt \ -X ssl.key.location=./user-write.key \ -X ssl.endpoint.identification.algorithm=none ``` **Чтение сообщений** ```bash kcat -C \ -b :9093 \ -t write-test \ -o beginning \ -q \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` --- ## 9. Рекомендации * Минимизируйте набор разрешённых операций для повышения безопасности. * Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов. * Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**. * После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам. ---. См. [nubes_kafka_user](kafka_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](kafka_params_modify.md) +- `modify_topic` — # Инструкция по управлению топиками Apache Kafka через платформу ## 1. Общая информация --- Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам. Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API. --- ## Доступные операции - **create_topic** — Создание топика - **modify_topic** — Модификация топика - **delete_topic** — Удаление топика --- ## 2. Параметры создания топика Ниже перечислены параметры, которые необходимо указать при создании нового топика: * **Имя топика** Название топика. Должно быть уникальным в пределах кластера. Пример: `orders-events`. * **Партиции** Количество партиций топика. Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки. Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки. * **Реплики** Количество реплик для каждой партиции. Обычно равно количеству доступных брокеров или меньше. Пример: `3`. --- ## 4. Параметры модификации топика Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными: * **Имя топика** Название топика. Пример: `orders-events`. * **Партиции** Новое количество партиций. Пример: `12`. * **Реплики** Новое количество реплик. Пример: `2`. --- ## 5. Рекомендации * Планируйте количество партиций с запасом — уменьшение их числа невозможно. * Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров. * Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки. ---. См. [nubes_kafka_topic](kafka_topic.md) +- `modify_user` — # Инструкция по управлению пользователями Apache Kafka. --- ## 1. Общая информация Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру. Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам. Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka. --- ## Доступные операции - **create_user** — Создание пользователя - **modify_user** — Модификация пользователя - **delete_user** — Удаление пользователя --- ## 2. Параметры создания пользователя Ниже перечислены параметры, которые необходимо указать при создании нового пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 3. Параметры модификации пользователя Ниже перечислены параметры, которые необходимо указать при модификации пользователя: * **Имя пользователя** Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав. Пример: `username-admin` * **Топик** Название топика, к которому будут назначены права для данного пользователя. Пример: `orders-events`. Использовать маску нельзя (`orders*`), только полное название или `*`. * **Операции** Список разрешённых операций на указанном топике. Пример: `Describe, Read, Write` * **Группа** Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп. Пример: `order-consumers`. * **Разрешённые хосты** Список хостов или подсетей, с которых разрешено подключение пользователя. Пример: `192.168.1.0/24` или `*`. --- ## 4. Параметры удалении пользователя Ниже перечислены параметры, которые необходимо указать при удалении пользователя: * **Имя пользователя** Имя удаляемого пользователя. Пример: `username-admin` ## 5. Выходные параметры После успешного создания пользователя платформа возвращает параметры, необходимые для подключения: * **ca.crt** Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении. Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **ca.p12** PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.p12 ``` * **ca.password** Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно. * **user.crt** Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka. Используется совместно с приватным ключом `user.key` для аутентификации. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.crt ``` * **user.key** Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo '<содержание поля>' | base64 -d > <название>.key ``` * **user.p12** PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka. Выдаеться в формате base64 для получения файла сертификата использовать команду ```bash echo "<содержание поля>" | base64 -d > <название>.p12 ``` * **user.password** Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно. Эти параметры используются для настройки TLS-подключения. --- ## 6. Пример конфигурации клиента внутри кластера Пример пода: ```yaml apiVersion: v1 kind: Pod metadata: name: kafka-4 spec: volumes: - name: work emptyDir: {} containers: - name: kcat image: quay.io/strimzi/kafka:latest-kafka-4.0.0 command: ["sleep", "5600"] volumeMounts: - name: work mountPath: /work restartPolicy: Never ``` Создаём под и сохраняем сертификаты: ```bash kubectl cp user.p12 kafka-4:/work/ kubectl cp ca.p12 kafka-4:/work/ ``` Создаём файлы конфигурации: ```bash cat > /work/user-read.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` ```bash cat > /work/user-write.properties <<'EOF' security.protocol=SSL ssl.keystore.type=PKCS12 ssl.keystore.location=/work/user-write.p12 ssl.keystore.password= ssl.truststore.type=PKCS12 ssl.truststore.location=/work/ca.p12 ssl.truststore.password= EOF ``` Листинг доступных топиков: ```bash /opt/kafka/bin/kafka-topics.sh \ --bootstrap-server kafkak8s-bootstrap-external:9093 \ --command-config /work/user-read.properties \ --list ``` Запись сообщений: ```bash /opt/kafka/bin/kafka-console-producer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --producer.config /work/user-write.properties ``` Чтение сообщений: ```bash /opt/kafka/bin/kafka-console-consumer.sh \ --bootstrap-server <адрес сервис брокера>:9093 \ --topic \ --from-beginning \ --group \ --consumer.config /work/user-read.properties \ --max-messages 50 ``` --- ## 7. Пример внешнего подключения с использованием **kcat** **Листинг топиков** ```bash kcat -b :9093 \ -L \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` **Запись сообщения** ```bash echo "hello from kcat" | kcat -P \ -b :9093 \ -t write-test \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-write.crt \ -X ssl.key.location=./user-write.key \ -X ssl.endpoint.identification.algorithm=none ``` **Чтение сообщений** ```bash kcat -C \ -b :9093 \ -t write-test \ -o beginning \ -q \ -X security.protocol=SSL \ -X ssl.ca.location=./ca.crt \ -X ssl.certificate.location=./user-read.crt \ -X ssl.key.location=./user-read.key \ -X ssl.endpoint.identification.algorithm=none ``` --- ## 9. Рекомендации * Минимизируйте набор разрешённых операций для повышения безопасности. * Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов. * Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**. * После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам. ---. См. [nubes_kafka_user](kafka_user.md) +- `resume` +- `suspend` diff --git a/generated/test/docs/kafka_outputs.md b/generated/test/docs/kafka_outputs.md new file mode 100644 index 0000000..7a36056 --- /dev/null +++ b/generated/test/docs/kafka_outputs.md @@ -0,0 +1,54 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.bootstrap"]` +- `state_out_flat["externalConnect.bootstrap.fqdn"]` +- `state_out_flat["externalConnect.bootstrap.ip"]` +- `state_out_flat["externalConnect.bootstrap.isExternal"]` +- `state_out_flat["externalConnect.bootstrap.uuid"]` +- `state_out_flat["internalConnect"]` +- `state_out_flat["kafkaTopics"]` +- `state_out_flat["kafkaTopics.test-events"]` +- `state_out_flat["kafkaTopics.test-events.partitions"]` +- `state_out_flat["kafkaTopics.test-events.replicas"]` +- `state_out_flat["kafkaUsers"]` +- `state_out_flat["kafkaUsers.usertest"]` +- `state_out_flat["kafkaUsers.usertest.accessHosts"]` +- `state_out_flat["kafkaUsers.usertest.group"]` +- `state_out_flat["kafkaUsers.usertest.nameTopic"]` +- `state_out_flat["kafkaUsers.usertest.operations"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["users"]` + diff --git a/generated/test/docs/kafka_params.md b/generated/test/docs/kafka_params.md new file mode 100644 index 0000000..25cb573 --- /dev/null +++ b/generated/test/docs/kafka_params.md @@ -0,0 +1,12 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Parameters + +- [Create params](kafka_params_create.md) +- [Modify params](kafka_params_modify.md) diff --git a/generated/test/docs/kafka_params_create.md b/generated/test/docs/kafka_params_create.md new file mode 100644 index 0000000..1a43a4e --- /dev/null +++ b/generated/test/docs/kafka_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
461resource_instancesintegerПример: `2`minvalue=1; value_list=1, 2, 3, 4, 5
462resource_memoryintegerПример: `2048`minvalue=1000
463resource_cpuintegerПример: `2000`minvalue=1000
464resource_diskintegerПример: `20`
466ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
469resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
465need_external_address_masterbooleanfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`value_list=false, true
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/kafka_params_modify.md b/generated/test/docs/kafka_params_modify.md new file mode 100644 index 0000000..e6ff431 --- /dev/null +++ b/generated/test/docs/kafka_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_kafka + +Service ID: `116` + +Service Name: ApacheKafka + +[Manual](kafka.md) | [Create params](kafka_params_create.md) | [Modify params](kafka_params_modify.md) | [Output params](kafka_outputs.md) | [Operations](kafka_ops.md) | [Example](kafka_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
501resource_instancesintegerПример: `2`value_list=1, 2, 3, 4, 5
502resource_memoryintegerПример: `2048`minvalue=1000
503resource_cpuПример: `2000`minvalue=1000
504resource_diskintegerПример: `20`
505need_external_address_masterbooleanФлаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`value_list=false, true
506ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
diff --git a/generated/test/docs/kafka_topic.md b/generated/test/docs/kafka_topic.md new file mode 100644 index 0000000..e56d01e --- /dev/null +++ b/generated/test/docs/kafka_topic.md @@ -0,0 +1,35 @@ +# Resource nubes_kafka_topic + +Service: `nubes_kafka` (ID: 116) + +[ApacheKafka (основной)](kafka.md) | [Operations](kafka_ops.md) | [Topic](kafka_topic.md) | [Example](kafka_topic_example.md) + +## Create params + +**Обязательные параметры** + + + + + + + +
IDCodeTypeDescriptionConstraints
523name_topicstringПример: `orders-events`
524partitionsintegerЧисло партиций можно увеличить, но уменьшить их нельзя.
525replicasintegerКоличество реплик не может привышать количество реплик брокера
+ +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
526name_topicstringПример: `orders-events`
527partitionsЧисло партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50`
528replicasКоличество партиций ≤ (число брокеров). Пример `2`
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
529name_topicstringНеобходимо выбрать топик, который необходимо удалить
diff --git a/generated/test/docs/kafka_topic_example.md b/generated/test/docs/kafka_topic_example.md new file mode 100644 index 0000000..32af8ff --- /dev/null +++ b/generated/test/docs/kafka_topic_example.md @@ -0,0 +1,33 @@ +# Resource nubes_kafka_topic — Example + +Service: `nubes_kafka` (ID: 116) + +[ApacheKafka (основной)](kafka.md) | [Operations](kafka_ops.md) | [Topic](kafka_topic.md) | [Example](kafka_topic_example.md) + +## Copy-ready manifest (`kafka_topic_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_kafka должен быть создан заранее +# resource "nubes_kafka" "baza" { ... } + +resource "nubes_kafka_topic" "example" { + kafka_id = nubes_kafka.baza.id + name_topic = "TODO" # Пример: `orders-events` + partitions = 1 # Число партиций можно увеличить, но уменьшить их нельзя. + replicas = 1 # Количество реплик не может привышать количество реплик брокера +} +``` diff --git a/generated/test/docs/kafka_user.md b/generated/test/docs/kafka_user.md new file mode 100644 index 0000000..46a21d0 --- /dev/null +++ b/generated/test/docs/kafka_user.md @@ -0,0 +1,39 @@ +# Resource nubes_kafka_user + +Service: `nubes_kafka` (ID: 116) + +[ApacheKafka (основной)](kafka.md) | [Operations](kafka_ops.md) | [User](kafka_user.md) | [Example](kafka_user_example.md) + +## Create params + +**Обязательные параметры** + + + + + + + + + +
IDCodeTypeDescriptionConstraints
530usernamestringПример: `username-admin`
531name_topicstringПример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`
532operationsstringПример: `Create, Describe, Read, Write`
533groupstringПример: `order-consumers` или `*`
534access_hostsstringПример: `192.168.1.0/24` или `*`
+ +## Modify params + + + + + + + + + +
IDCodeTypeDescriptionConstraints
535name_topicstringПример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`
536operationsstringПример: `Create, Describe, Read, Write`
537groupstringПример: `order-consumers` или `*`
538access_hostsstringПример: `192.168.1.0/24` или `*`
539usernamestringПример: `username-admin`
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
540usernamestringПример: `username-admin`
diff --git a/generated/test/docs/kafka_user_example.md b/generated/test/docs/kafka_user_example.md new file mode 100644 index 0000000..28bada9 --- /dev/null +++ b/generated/test/docs/kafka_user_example.md @@ -0,0 +1,35 @@ +# Resource nubes_kafka_user — Example + +Service: `nubes_kafka` (ID: 116) + +[ApacheKafka (основной)](kafka.md) | [Operations](kafka_ops.md) | [User](kafka_user.md) | [Example](kafka_user_example.md) + +## Copy-ready manifest (`kafka_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_kafka должен быть создан заранее +# resource "nubes_kafka" "baza" { ... } + +resource "nubes_kafka_user" "example" { + kafka_id = nubes_kafka.baza.id + username = "TODO" # Пример: `username-admin` + name_topic = "TODO" # Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*` + operations = "TODO" # Пример: `Create, Describe, Read, Write` + group = "TODO" # Пример: `order-consumers` или `*` + access_hosts = "TODO" # Пример: `192.168.1.0/24` или `*` +} +``` diff --git a/generated/test/docs/lucee.md b/generated/test/docs/lucee.md new file mode 100644 index 0000000..ca3542d --- /dev/null +++ b/generated/test/docs/lucee.md @@ -0,0 +1,13 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/lucee_example.md b/generated/test/docs/lucee_example.md new file mode 100644 index 0000000..22320a4 --- /dev/null +++ b/generated/test/docs/lucee_example.md @@ -0,0 +1,49 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Copy-ready manifest (`lucee_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_lucee" "baza" { + startup_configuration = "TODO" + cluster_configuration = "TODO" + access_configuration = "TODO" + app_configuration = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + json_env = {} # Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_lucee.baza.state_params["имя_ключа"] +nubes_lucee.baza.state_out["имя_ключа"] +nubes_lucee.baza.state_params_flat["имя_ключа"] +nubes_lucee.baza.state_out_flat["имя_ключа"] +nubes_lucee.baza.vault_secrets["имя_ключа"] +nubes_lucee.baza.vault_url +nubes_lucee.baza.vault_user_path +nubes_lucee.baza.vault_fields +``` diff --git a/generated/test/docs/lucee_ops.md b/generated/test/docs/lucee_ops.md new file mode 100644 index 0000000..a2e0373 --- /dev/null +++ b/generated/test/docs/lucee_ops.md @@ -0,0 +1,18 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](lucee_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](lucee_params_modify.md) +- `reconcile` +- `redeploy` +- `restart` +- `resume` +- `suspend` diff --git a/generated/test/docs/lucee_outputs.md b/generated/test/docs/lucee_outputs.md new file mode 100644 index 0000000..838f21d --- /dev/null +++ b/generated/test/docs/lucee_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/test/docs/lucee_params.md b/generated/test/docs/lucee_params.md new file mode 100644 index 0000000..730e3b2 --- /dev/null +++ b/generated/test/docs/lucee_params.md @@ -0,0 +1,12 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Parameters + +- [Create params](lucee_params_create.md) +- [Modify params](lucee_params_modify.md) diff --git a/generated/test/docs/lucee_params_create.md b/generated/test/docs/lucee_params_create.md new file mode 100644 index 0000000..755d619 --- /dev/null +++ b/generated/test/docs/lucee_params_create.md @@ -0,0 +1,39 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
1019startup_configurationmap-fixed
1020cluster_configurationmap-fixed
1021access_configurationmap-fixed
1022app_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
1023json_envmap{}Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/lucee_params_modify.md b/generated/test/docs/lucee_params_modify.md new file mode 100644 index 0000000..4bda31b --- /dev/null +++ b/generated/test/docs/lucee_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_lucee + +Service ID: `94` + +Service Name: Lucee + +[Manual](lucee.md) | [Create params](lucee_params_create.md) | [Modify params](lucee_params_modify.md) | [Output params](lucee_outputs.md) | [Operations](lucee_ops.md) | [Example](lucee_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
1024cluster_configurationmap-fixed
1025app_configurationmap-fixed
1026json_envmapПередаётся в формате КЛЮЧ:ЗНАЧЕНИЕ
diff --git a/generated/test/docs/mariadb.md b/generated/test/docs/mariadb.md new file mode 100644 index 0000000..9161094 --- /dev/null +++ b/generated/test/docs/mariadb.md @@ -0,0 +1,13 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## MAN + +
+Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление. +
diff --git a/generated/test/docs/mariadb_database.md b/generated/test/docs/mariadb_database.md new file mode 100644 index 0000000..092f130 --- /dev/null +++ b/generated/test/docs/mariadb_database.md @@ -0,0 +1,27 @@ +# Resource nubes_mariadb_database + +Service: `nubes_mariadb` (ID: 115) + +[Mariadb (основной)](mariadb.md) | [Operations](mariadb_ops.md) | [Database](mariadb_database.md) | [Example](mariadb_database_example.md) + +## Create params + +**Обязательные параметры** + +None. + +**Параметры со значением по умолчанию** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
734db_namestringdb-Пример: `db01`regex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
745db_namestringВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ
diff --git a/generated/test/docs/mariadb_database_example.md b/generated/test/docs/mariadb_database_example.md new file mode 100644 index 0000000..0cf5188 --- /dev/null +++ b/generated/test/docs/mariadb_database_example.md @@ -0,0 +1,33 @@ +# Resource nubes_mariadb_database — Example + +Service: `nubes_mariadb` (ID: 115) + +[Mariadb (основной)](mariadb.md) | [Operations](mariadb_ops.md) | [Database](mariadb_database.md) | [Example](mariadb_database_example.md) + +## Copy-ready manifest (`mariadb_database_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_mariadb должен быть создан заранее +# resource "nubes_mariadb" "baza" { ... } + +resource "nubes_mariadb_database" "example" { + mariadb_id = nubes_mariadb.baza.id + + # Параметры со значением по умолчанию + db_name = "db-" # Пример: `db01` +} +``` diff --git a/generated/test/docs/mariadb_example.md b/generated/test/docs/mariadb_example.md new file mode 100644 index 0000000..e5ba3f5 --- /dev/null +++ b/generated/test/docs/mariadb_example.md @@ -0,0 +1,48 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Copy-ready manifest (`mariadb_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_mariadb" "baza" { + startup_configuration = "TODO" + cluster_configuration = "TODO" + access_configuration = "TODO" + mariadb_configuration = "TODO" + backup_configuration = "TODO" + autoscale_configuration = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_mariadb.baza.state_params["имя_ключа"] +nubes_mariadb.baza.state_out["имя_ключа"] +nubes_mariadb.baza.state_params_flat["имя_ключа"] +nubes_mariadb.baza.state_out_flat["имя_ключа"] +nubes_mariadb.baza.vault_secrets["имя_ключа"] +nubes_mariadb.baza.vault_url +nubes_mariadb.baza.vault_user_path +nubes_mariadb.baza.vault_fields +``` diff --git a/generated/test/docs/mariadb_ops.md b/generated/test/docs/mariadb_ops.md new file mode 100644 index 0000000..5ae1622 --- /dev/null +++ b/generated/test/docs/mariadb_ops.md @@ -0,0 +1,20 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](mariadb_params_create.md) +- `create_database` — . См. [nubes_mariadb_database](mariadb_database.md) +- `create_user` — . См. [nubes_mariadb_user](mariadb_user.md) +- `delete` +- `delete_database` — . См. [nubes_mariadb_database](mariadb_database.md) +- `delete_user` — . См. [nubes_mariadb_user](mariadb_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](mariadb_params_modify.md) +- `reconcile` +- `resume` +- `suspend` diff --git a/generated/test/docs/mariadb_outputs.md b/generated/test/docs/mariadb_outputs.md new file mode 100644 index 0000000..38f0606 --- /dev/null +++ b/generated/test/docs/mariadb_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/mariadb_params.md b/generated/test/docs/mariadb_params.md new file mode 100644 index 0000000..1d0477b --- /dev/null +++ b/generated/test/docs/mariadb_params.md @@ -0,0 +1,12 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Parameters + +- [Create params](mariadb_params_create.md) +- [Modify params](mariadb_params_modify.md) diff --git a/generated/test/docs/mariadb_params_create.md b/generated/test/docs/mariadb_params_create.md new file mode 100644 index 0000000..2bc0c0a --- /dev/null +++ b/generated/test/docs/mariadb_params_create.md @@ -0,0 +1,37 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
877startup_configurationmap-fixed
878cluster_configurationmap-fixed
879access_configurationmap-fixed
880mariadb_configurationmap-fixed
881backup_configurationmap-fixed
882autoscale_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/mariadb_params_modify.md b/generated/test/docs/mariadb_params_modify.md new file mode 100644 index 0000000..65a66e7 --- /dev/null +++ b/generated/test/docs/mariadb_params_modify.md @@ -0,0 +1,19 @@ +# Resource nubes_mariadb + +Service ID: `115` + +Service Name: Mariadb + +[Manual](mariadb.md) | [Create params](mariadb_params_create.md) | [Modify params](mariadb_params_modify.md) | [Output params](mariadb_outputs.md) | [Operations](mariadb_ops.md) | [Example](mariadb_example.md) + +## Modify params + + + + + + + + + +
IDCodeTypeDescriptionConstraints
883cluster_configurationmap-fixed
884access_configurationmap-fixed
885mariadb_configurationmap-fixed
886backup_configurationmap-fixed
887autoscale_configurationmap-fixed
diff --git a/generated/test/docs/mariadb_user.md b/generated/test/docs/mariadb_user.md new file mode 100644 index 0000000..c590f5e --- /dev/null +++ b/generated/test/docs/mariadb_user.md @@ -0,0 +1,33 @@ +# Resource nubes_mariadb_user + +Service: `nubes_mariadb` (ID: 115) + +[Mariadb (основной)](mariadb.md) | [Operations](mariadb_ops.md) | [User](mariadb_user.md) | [Example](mariadb_user_example.md) + +## Create params + +**Обязательные параметры** + + + + + +
IDCodeTypeDescriptionConstraints
736passwordstringСекрет будет записан в хранилище секретов и не будет отображаться публичноmaxlength=62; minlength=8
+ +**Параметры со значением по умолчанию** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
735usernamestringuserПример: `myusername`regex=^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$; maxlength=62; minlength=2
737db_namearray-map-fixed[{}]Если базы не существует - при выполнении операции вернётся ошибка
738access_hostsstring%Пример: `%`
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
744usernamestringИмя пользователя, созданное через операцию `create_user`
diff --git a/generated/test/docs/mariadb_user_example.md b/generated/test/docs/mariadb_user_example.md new file mode 100644 index 0000000..e16d466 --- /dev/null +++ b/generated/test/docs/mariadb_user_example.md @@ -0,0 +1,36 @@ +# Resource nubes_mariadb_user — Example + +Service: `nubes_mariadb` (ID: 115) + +[Mariadb (основной)](mariadb.md) | [Operations](mariadb_ops.md) | [User](mariadb_user.md) | [Example](mariadb_user_example.md) + +## Copy-ready manifest (`mariadb_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_mariadb должен быть создан заранее +# resource "nubes_mariadb" "baza" { ... } + +resource "nubes_mariadb_user" "example" { + mariadb_id = nubes_mariadb.baza.id + password = "TODO" # Секрет будет записан в хранилище секретов и не будет отображаться публично + + # Параметры со значением по умолчанию + username = "user" # Пример: `myusername` + db_name = [{}] # Необходимо указать массив существующих баз + access_hosts = "%" # Пример: `%` +} +``` diff --git a/generated/test/docs/mongodb.md b/generated/test/docs/mongodb.md new file mode 100644 index 0000000..3502e9e --- /dev/null +++ b/generated/test/docs/mongodb.md @@ -0,0 +1,13 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## MAN + +
+# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание базы
* **delete** — удаление базы данных
* **suspend** — остановка базы данных
* **resume** — запуск базы данных
* **modify** — изменение базы данных

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания базы данных

Эти параметры необходимо указать при создании базы данных.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры модификации базы данных

Все параметры, которые вы указываете при модификации, являются необязательными — система автоматически подставит ранее сохранённые значения, и при необходимости вы можете изменить их на нужные.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных указывается в гигабайтах. Его можно увеличить при необходимости, однако уменьшить уже выделенный размер невозможно.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания базы данных платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю. +
diff --git a/generated/test/docs/mongodb_example.md b/generated/test/docs/mongodb_example.md new file mode 100644 index 0000000..895b483 --- /dev/null +++ b/generated/test/docs/mongodb_example.md @@ -0,0 +1,50 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Copy-ready manifest (`mongodb_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_mongodb" "baza" { + resource_instances = 1 # Пример: `2` + resource_memory = 1 # Пример: `2048` + resource_cpu = 1 # Пример: `1000` + resource_disk = 1 # Пример: `20` + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + master_access_list = [] # Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем. +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_mongodb.baza.state_params["имя_ключа"] +nubes_mongodb.baza.state_out["имя_ключа"] +nubes_mongodb.baza.state_params_flat["имя_ключа"] +nubes_mongodb.baza.state_out_flat["имя_ключа"] +nubes_mongodb.baza.vault_secrets["имя_ключа"] +nubes_mongodb.baza.vault_url +nubes_mongodb.baza.vault_user_path +nubes_mongodb.baza.vault_fields +``` diff --git a/generated/test/docs/mongodb_ops.md b/generated/test/docs/mongodb_ops.md new file mode 100644 index 0000000..fb06e5b --- /dev/null +++ b/generated/test/docs/mongodb_ops.md @@ -0,0 +1,18 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](mongodb_params_create.md) +- `create_user` — . См. [nubes_mongodb_user](mongodb_user.md) +- `delete` +- `delete_user` — . См. [nubes_mongodb_user](mongodb_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](mongodb_params_modify.md) +- `modify_user` — . См. [nubes_mongodb_user](mongodb_user.md) +- `resume` +- `suspend` diff --git a/generated/test/docs/mongodb_outputs.md b/generated/test/docs/mongodb_outputs.md new file mode 100644 index 0000000..2a6b2dc --- /dev/null +++ b/generated/test/docs/mongodb_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["internalConnect"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/test/docs/mongodb_params.md b/generated/test/docs/mongodb_params.md new file mode 100644 index 0000000..b595ec1 --- /dev/null +++ b/generated/test/docs/mongodb_params.md @@ -0,0 +1,12 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Parameters + +- [Create params](mongodb_params_create.md) +- [Modify params](mongodb_params_modify.md) diff --git a/generated/test/docs/mongodb_params_create.md b/generated/test/docs/mongodb_params_create.md new file mode 100644 index 0000000..4cfcb00 --- /dev/null +++ b/generated/test/docs/mongodb_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
487resource_instancesintegerПример: `2`minvalue=1; maxvalue=7
588resource_memoryintegerПример: `2048`minvalue=512
589resource_cpuintegerПример: `1000`
590resource_diskintegerПример: `20`
595resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
646ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
889master_access_listjson[]Передаётся как массив строк. Пример: [&quot;192.168.0.0&quot;, &quot;192.168.0.1&quot;]
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/mongodb_params_modify.md b/generated/test/docs/mongodb_params_modify.md new file mode 100644 index 0000000..9bff5f5 --- /dev/null +++ b/generated/test/docs/mongodb_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_mongodb + +Service ID: `92` + +Service Name: MongoDB + +[Manual](mongodb.md) | [Create params](mongodb_params_create.md) | [Modify params](mongodb_params_modify.md) | [Output params](mongodb_outputs.md) | [Operations](mongodb_ops.md) | [Example](mongodb_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
648resource_instancesintegerПример: `2`
649resource_memoryintegerПример: `2048`minvalue=512
650resource_cpuintegerПример: `1000`minvalue=500
651resource_diskintegerПример: `20`
653ip_space_name_masterstringЕсли включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`
890master_access_listjsonПередаётся как массив строк. Пример: [&quot;192.168.0.0&quot;, &quot;192.168.0.1&quot;]
diff --git a/generated/test/docs/mongodb_user.md b/generated/test/docs/mongodb_user.md new file mode 100644 index 0000000..ece4f31 --- /dev/null +++ b/generated/test/docs/mongodb_user.md @@ -0,0 +1,35 @@ +# Resource nubes_mongodb_user + +Service: `nubes_mongodb` (ID: 92) + +[MongoDB (основной)](mongodb.md) | [Operations](mongodb_ops.md) | [User](mongodb_user.md) | [Example](mongodb_user_example.md) + +## Create params + +**Обязательные параметры** + + + + + + + +
IDCodeTypeDescriptionConstraints
635usernamestring
636db_namestring
643rolestringvalue_list=dbOwner, read, readWrite
+ +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
640usernamestring
641db_namestring
642rolestringvalue_list=dbOwner, read, readWrite
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
644usernamestring
diff --git a/generated/test/docs/mongodb_user_example.md b/generated/test/docs/mongodb_user_example.md new file mode 100644 index 0000000..e59f2c4 --- /dev/null +++ b/generated/test/docs/mongodb_user_example.md @@ -0,0 +1,33 @@ +# Resource nubes_mongodb_user — Example + +Service: `nubes_mongodb` (ID: 92) + +[MongoDB (основной)](mongodb.md) | [Operations](mongodb_ops.md) | [User](mongodb_user.md) | [Example](mongodb_user_example.md) + +## Copy-ready manifest (`mongodb_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_mongodb должен быть создан заранее +# resource "nubes_mongodb" "baza" { ... } + +resource "nubes_mongodb_user" "example" { + mongodb_id = nubes_mongodb.baza.id + username = "TODO" + db_name = "TODO" + role = "dbOwner" +} +``` diff --git a/generated/test/docs/nextcloud.md b/generated/test/docs/nextcloud.md new file mode 100644 index 0000000..a494623 --- /dev/null +++ b/generated/test/docs/nextcloud.md @@ -0,0 +1,13 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## MAN + +
+Ресурсная платформа: k8s. Автоматизация: helm chart +
diff --git a/generated/test/docs/nextcloud_backup.md b/generated/test/docs/nextcloud_backup.md new file mode 100644 index 0000000..4145cbb --- /dev/null +++ b/generated/test/docs/nextcloud_backup.md @@ -0,0 +1,19 @@ +# Resource nubes_nextcloud_backup + +Service: `nubes_nextcloud` (ID: 50) + +[Nextcloud (основной)](nextcloud.md) | [Operations](nextcloud_ops.md) | [Backup](nextcloud_backup.md) | [Example](nextcloud_backup_example.md) + +## Create params + +**Обязательные параметры** + + + + + +
IDCodeTypeDescriptionConstraints
1077startup_configurationmap-fixed
+ +## Delete params + +None. diff --git a/generated/test/docs/nextcloud_backup_example.md b/generated/test/docs/nextcloud_backup_example.md new file mode 100644 index 0000000..53c1b33 --- /dev/null +++ b/generated/test/docs/nextcloud_backup_example.md @@ -0,0 +1,31 @@ +# Resource nubes_nextcloud_backup — Example + +Service: `nubes_nextcloud` (ID: 50) + +[Nextcloud (основной)](nextcloud.md) | [Operations](nextcloud_ops.md) | [Backup](nextcloud_backup.md) | [Example](nextcloud_backup_example.md) + +## Copy-ready manifest (`nextcloud_backup_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_nextcloud должен быть создан заранее +# resource "nubes_nextcloud" "baza" { ... } + +resource "nubes_nextcloud_backup" "example" { + nextcloud_id = nubes_nextcloud.baza.id + startup_configuration = "TODO" +} +``` diff --git a/generated/test/docs/nextcloud_example.md b/generated/test/docs/nextcloud_example.md new file mode 100644 index 0000000..abbc7a1 --- /dev/null +++ b/generated/test/docs/nextcloud_example.md @@ -0,0 +1,46 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Copy-ready manifest (`nextcloud_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_nextcloud" "baza" { + startup_configuration = "TODO" + cluster_configuration = "TODO" + access_configuration = "TODO" + app_configuration = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nextcloud.baza.state_params["имя_ключа"] +nubes_nextcloud.baza.state_out["имя_ключа"] +nubes_nextcloud.baza.state_params_flat["имя_ключа"] +nubes_nextcloud.baza.state_out_flat["имя_ключа"] +nubes_nextcloud.baza.vault_secrets["имя_ключа"] +nubes_nextcloud.baza.vault_url +nubes_nextcloud.baza.vault_user_path +nubes_nextcloud.baza.vault_fields +``` diff --git a/generated/test/docs/nextcloud_ops.md b/generated/test/docs/nextcloud_ops.md new file mode 100644 index 0000000..47b0bce --- /dev/null +++ b/generated/test/docs/nextcloud_ops.md @@ -0,0 +1,19 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nextcloud_params_create.md) +- `create_backup` — Перед запуском обязательно надо перевести экземпляр Nextcloud в статус 'suspended'. См. [nubes_nextcloud_backup](nextcloud_backup.md) +- `delete` — DEV-Ветка удаления Nextcloud +- `modify` — Изменение кластера. Параметры: [Modify params](nextcloud_params_modify.md) +- `reconcile` +- `recovery` +- `restart` +- `resume` +- `suspend` diff --git a/generated/test/docs/nextcloud_outputs.md b/generated/test/docs/nextcloud_outputs.md new file mode 100644 index 0000000..1a8707b --- /dev/null +++ b/generated/test/docs/nextcloud_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/nextcloud_params.md b/generated/test/docs/nextcloud_params.md new file mode 100644 index 0000000..2c1eb71 --- /dev/null +++ b/generated/test/docs/nextcloud_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Parameters + +- [Create params](nextcloud_params_create.md) +- [Modify params](nextcloud_params_modify.md) diff --git a/generated/test/docs/nextcloud_params_create.md b/generated/test/docs/nextcloud_params_create.md new file mode 100644 index 0000000..c6723ab --- /dev/null +++ b/generated/test/docs/nextcloud_params_create.md @@ -0,0 +1,35 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
1055startup_configurationmap-fixed
1056cluster_configurationmap-fixed
1057access_configurationmap-fixed
1058app_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/nextcloud_params_modify.md b/generated/test/docs/nextcloud_params_modify.md new file mode 100644 index 0000000..47d56d1 --- /dev/null +++ b/generated/test/docs/nextcloud_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_nextcloud + +Service ID: `50` + +Service Name: Nextcloud + +[Manual](nextcloud.md) | [Create params](nextcloud_params_create.md) | [Modify params](nextcloud_params_modify.md) | [Output params](nextcloud_outputs.md) | [Operations](nextcloud_ops.md) | [Example](nextcloud_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
1059cluster_configurationmap-fixed
1060app_configurationmap-fixed
diff --git a/generated/test/docs/nifi.md b/generated/test/docs/nifi.md new file mode 100644 index 0000000..cf2c590 --- /dev/null +++ b/generated/test/docs/nifi.md @@ -0,0 +1,13 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/nifi_example.md b/generated/test/docs/nifi_example.md new file mode 100644 index 0000000..0897886 --- /dev/null +++ b/generated/test/docs/nifi_example.md @@ -0,0 +1,46 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Copy-ready manifest (`nifi_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_nifi" "baza" { + kafka_uid = "TODO" # Выбрать кластер из списка + partitions = 1 # Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` + replicas = 1 # Количество партиций ≤ (число брокеров). Пример `2` + name_topic = "TODO" # Пример: `orders-events` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nifi.baza.state_params["имя_ключа"] +nubes_nifi.baza.state_out["имя_ключа"] +nubes_nifi.baza.state_params_flat["имя_ключа"] +nubes_nifi.baza.state_out_flat["имя_ключа"] +nubes_nifi.baza.vault_secrets["имя_ключа"] +nubes_nifi.baza.vault_url +nubes_nifi.baza.vault_user_path +nubes_nifi.baza.vault_fields +``` diff --git a/generated/test/docs/nifi_ops.md b/generated/test/docs/nifi_ops.md new file mode 100644 index 0000000..a896c6a --- /dev/null +++ b/generated/test/docs/nifi_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nifi_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](nifi_params_modify.md) diff --git a/generated/test/docs/nifi_outputs.md b/generated/test/docs/nifi_outputs.md new file mode 100644 index 0000000..976b25e --- /dev/null +++ b/generated/test/docs/nifi_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/nifi_params.md b/generated/test/docs/nifi_params.md new file mode 100644 index 0000000..4dcadac --- /dev/null +++ b/generated/test/docs/nifi_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Parameters + +- [Create params](nifi_params_create.md) +- [Modify params](nifi_params_modify.md) diff --git a/generated/test/docs/nifi_params_create.md b/generated/test/docs/nifi_params_create.md new file mode 100644 index 0000000..d5c4baa --- /dev/null +++ b/generated/test/docs/nifi_params_create.md @@ -0,0 +1,23 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
471kafka_uiduuidВыбрать кластер из спискаref_svc_id=116
472partitionsintegerОбратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.
473replicasintegerКоличество партиций ≤ (число брокеров). Пример `2`
474name_topicstringПример: `orders-events`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/test/docs/nifi_params_modify.md b/generated/test/docs/nifi_params_modify.md new file mode 100644 index 0000000..7f34ef0 --- /dev/null +++ b/generated/test/docs/nifi_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_nifi + +Service ID: `117` + +Service Name: Nifi + +[Manual](nifi.md) | [Create params](nifi_params_create.md) | [Modify params](nifi_params_modify.md) | [Output params](nifi_outputs.md) | [Operations](nifi_ops.md) | [Example](nifi_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
477partitionsintegerОбратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.
478replicasintegerКоличество партиций ≤ (число брокеров). Пример `2`
diff --git a/generated/test/docs/nodejs.md b/generated/test/docs/nodejs.md new file mode 100644 index 0000000..a1c6804 --- /dev/null +++ b/generated/test/docs/nodejs.md @@ -0,0 +1,13 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## MAN + +
+Развертывает контейнер в k8s кластере с node, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. +
diff --git a/generated/test/docs/nodejs_example.md b/generated/test/docs/nodejs_example.md new file mode 100644 index 0000000..3bfda9f --- /dev/null +++ b/generated/test/docs/nodejs_example.md @@ -0,0 +1,49 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Copy-ready manifest (`nodejs_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_nodejs" "baza" { + startup_configuration = "TODO" + cluster_configuration = "TODO" + access_configuration = "TODO" + app_configuration = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + json_env = {} # Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nodejs.baza.state_params["имя_ключа"] +nubes_nodejs.baza.state_out["имя_ключа"] +nubes_nodejs.baza.state_params_flat["имя_ключа"] +nubes_nodejs.baza.state_out_flat["имя_ключа"] +nubes_nodejs.baza.vault_secrets["имя_ключа"] +nubes_nodejs.baza.vault_url +nubes_nodejs.baza.vault_user_path +nubes_nodejs.baza.vault_fields +``` diff --git a/generated/test/docs/nodejs_ops.md b/generated/test/docs/nodejs_ops.md new file mode 100644 index 0000000..46c51e1 --- /dev/null +++ b/generated/test/docs/nodejs_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nodejs_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](nodejs_params_modify.md) +- `reconcile` +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/test/docs/nodejs_outputs.md b/generated/test/docs/nodejs_outputs.md new file mode 100644 index 0000000..5fa8a14 --- /dev/null +++ b/generated/test/docs/nodejs_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/test/docs/nodejs_params.md b/generated/test/docs/nodejs_params.md new file mode 100644 index 0000000..daf96ca --- /dev/null +++ b/generated/test/docs/nodejs_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Parameters + +- [Create params](nodejs_params_create.md) +- [Modify params](nodejs_params_modify.md) diff --git a/generated/test/docs/nodejs_params_create.md b/generated/test/docs/nodejs_params_create.md new file mode 100644 index 0000000..6151786 --- /dev/null +++ b/generated/test/docs/nodejs_params_create.md @@ -0,0 +1,39 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + +
IDCodeTypeDescriptionConstraints
1028startup_configurationmap-fixed
1029cluster_configurationmap-fixed
1030access_configurationmap-fixed
1031app_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
1032json_envmap{}Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/nodejs_params_modify.md b/generated/test/docs/nodejs_params_modify.md new file mode 100644 index 0000000..b0158e2 --- /dev/null +++ b/generated/test/docs/nodejs_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_nodejs + +Service ID: `95` + +Service Name: NodeJS + +[Manual](nodejs.md) | [Create params](nodejs_params_create.md) | [Modify params](nodejs_params_modify.md) | [Output params](nodejs_outputs.md) | [Operations](nodejs_ops.md) | [Example](nodejs_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
1033cluster_configurationmap-fixed
1034app_configurationmap-fixed
1035json_envmapПередаётся в формате КЛЮЧ:ЗНАЧЕНИЕ
diff --git a/generated/test/docs/nodered.md b/generated/test/docs/nodered.md new file mode 100644 index 0000000..54ecff3 --- /dev/null +++ b/generated/test/docs/nodered.md @@ -0,0 +1,13 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/nodered_example.md b/generated/test/docs/nodered_example.md new file mode 100644 index 0000000..554f430 --- /dev/null +++ b/generated/test/docs/nodered_example.md @@ -0,0 +1,50 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Copy-ready manifest (`nodered_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_nodered" "baza" { + resource_realm = "TODO" # Платформа для развертывания + domain = "TODO" # Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_nodered.baza.state_params["имя_ключа"] +nubes_nodered.baza.state_out["имя_ключа"] +nubes_nodered.baza.state_params_flat["имя_ключа"] +nubes_nodered.baza.state_out_flat["имя_ключа"] +nubes_nodered.baza.vault_secrets["имя_ключа"] +nubes_nodered.baza.vault_url +nubes_nodered.baza.vault_user_path +nubes_nodered.baza.vault_fields +``` diff --git a/generated/test/docs/nodered_ops.md b/generated/test/docs/nodered_ops.md new file mode 100644 index 0000000..2d6074e --- /dev/null +++ b/generated/test/docs/nodered_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](nodered_params_create.md) +- `delete` diff --git a/generated/test/docs/nodered_outputs.md b/generated/test/docs/nodered_outputs.md new file mode 100644 index 0000000..74e4213 --- /dev/null +++ b/generated/test/docs/nodered_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/nodered_params.md b/generated/test/docs/nodered_params.md new file mode 100644 index 0000000..60a797d --- /dev/null +++ b/generated/test/docs/nodered_params.md @@ -0,0 +1,12 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Parameters + +- [Create params](nodered_params_create.md) +- [Modify params](nodered_params_modify.md) diff --git a/generated/test/docs/nodered_params_create.md b/generated/test/docs/nodered_params_create.md new file mode 100644 index 0000000..f976ccb --- /dev/null +++ b/generated/test/docs/nodered_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
196resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
197domainstringНаименование поддомена, будет добавлено в k8stemplate.dev.nubes.ruregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
193resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=100; maxvalue=4000
194resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
195resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
323resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
diff --git a/generated/test/docs/nodered_params_modify.md b/generated/test/docs/nodered_params_modify.md new file mode 100644 index 0000000..be00f3b --- /dev/null +++ b/generated/test/docs/nodered_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_nodered + +Service ID: `97` + +Service Name: NodeRed + +[Manual](nodered.md) | [Create params](nodered_params_create.md) | [Modify params](nodered_params_modify.md) | [Output params](nodered_outputs.md) | [Operations](nodered_ops.md) | [Example](nodered_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/openwhisk.md b/generated/test/docs/openwhisk.md new file mode 100644 index 0000000..059ed46 --- /dev/null +++ b/generated/test/docs/openwhisk.md @@ -0,0 +1,13 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## MAN + +
+Добавление и удаления пользователя для работы с Openwhisk +
diff --git a/generated/test/docs/openwhisk_example.md b/generated/test/docs/openwhisk_example.md new file mode 100644 index 0000000..0eda09d --- /dev/null +++ b/generated/test/docs/openwhisk_example.md @@ -0,0 +1,42 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Copy-ready manifest (`openwhisk_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_openwhisk" "baza" { +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_openwhisk.baza.state_params["имя_ключа"] +nubes_openwhisk.baza.state_out["имя_ключа"] +nubes_openwhisk.baza.state_params_flat["имя_ключа"] +nubes_openwhisk.baza.state_out_flat["имя_ключа"] +nubes_openwhisk.baza.vault_secrets["имя_ключа"] +nubes_openwhisk.baza.vault_url +nubes_openwhisk.baza.vault_user_path +nubes_openwhisk.baza.vault_fields +``` diff --git a/generated/test/docs/openwhisk_ops.md b/generated/test/docs/openwhisk_ops.md new file mode 100644 index 0000000..e931c78 --- /dev/null +++ b/generated/test/docs/openwhisk_ops.md @@ -0,0 +1,10 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Operations + diff --git a/generated/test/docs/openwhisk_outputs.md b/generated/test/docs/openwhisk_outputs.md new file mode 100644 index 0000000..e2cdfe5 --- /dev/null +++ b/generated/test/docs/openwhisk_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/openwhisk_params.md b/generated/test/docs/openwhisk_params.md new file mode 100644 index 0000000..1a9b0e5 --- /dev/null +++ b/generated/test/docs/openwhisk_params.md @@ -0,0 +1,12 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Parameters + +- [Create params](openwhisk_params_create.md) +- [Modify params](openwhisk_params_modify.md) diff --git a/generated/test/docs/openwhisk_params_create.md b/generated/test/docs/openwhisk_params_create.md new file mode 100644 index 0000000..f6d47cf --- /dev/null +++ b/generated/test/docs/openwhisk_params_create.md @@ -0,0 +1,16 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + +None. + +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/test/docs/openwhisk_params_modify.md b/generated/test/docs/openwhisk_params_modify.md new file mode 100644 index 0000000..7791043 --- /dev/null +++ b/generated/test/docs/openwhisk_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_openwhisk + +Service ID: `100` + +Service Name: Serverless Openwhisk (openwhisk_tenant) + +[Manual](openwhisk.md) | [Create params](openwhisk_params_create.md) | [Modify params](openwhisk_params_modify.md) | [Output params](openwhisk_outputs.md) | [Operations](openwhisk_ops.md) | [Example](openwhisk_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/pgadmin.md b/generated/test/docs/pgadmin.md new file mode 100644 index 0000000..5c2feaf --- /dev/null +++ b/generated/test/docs/pgadmin.md @@ -0,0 +1,13 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## MAN + +
+Инструмент с графическим интерфейсом для управления базами данных PostgreSQL +
diff --git a/generated/test/docs/pgadmin_example.md b/generated/test/docs/pgadmin_example.md new file mode 100644 index 0000000..8366795 --- /dev/null +++ b/generated/test/docs/pgadmin_example.md @@ -0,0 +1,51 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Copy-ready manifest (`pgadmin_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_pgadmin" "baza" { + resource_realm = "TODO" # Платформа для развертывания + login = "TODO" # Логин для подключения к pgAdmin, в формате email (login@email.net) + password = "TODO" # Пароль для подключения к pgAdmin + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + resource_cpu = 500 # Квота ядра пода (milicores). 1000Mili = 1 Ядро + resource_memory = 512 # Квота памяти пода (MBytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_pgadmin.baza.state_params["имя_ключа"] +nubes_pgadmin.baza.state_out["имя_ключа"] +nubes_pgadmin.baza.state_params_flat["имя_ключа"] +nubes_pgadmin.baza.state_out_flat["имя_ключа"] +nubes_pgadmin.baza.vault_secrets["имя_ключа"] +nubes_pgadmin.baza.vault_url +nubes_pgadmin.baza.vault_user_path +nubes_pgadmin.baza.vault_fields +``` diff --git a/generated/test/docs/pgadmin_ops.md b/generated/test/docs/pgadmin_ops.md new file mode 100644 index 0000000..7449f5e --- /dev/null +++ b/generated/test/docs/pgadmin_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](pgadmin_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](pgadmin_params_modify.md) +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/test/docs/pgadmin_outputs.md b/generated/test/docs/pgadmin_outputs.md new file mode 100644 index 0000000..a88297a --- /dev/null +++ b/generated/test/docs/pgadmin_outputs.md @@ -0,0 +1,34 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["connectionUrl"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + diff --git a/generated/test/docs/pgadmin_params.md b/generated/test/docs/pgadmin_params.md new file mode 100644 index 0000000..3afd95c --- /dev/null +++ b/generated/test/docs/pgadmin_params.md @@ -0,0 +1,12 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Parameters + +- [Create params](pgadmin_params_create.md) +- [Modify params](pgadmin_params_modify.md) diff --git a/generated/test/docs/pgadmin_params_create.md b/generated/test/docs/pgadmin_params_create.md new file mode 100644 index 0000000..f87da9c --- /dev/null +++ b/generated/test/docs/pgadmin_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
169resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
170loginstringЛогин для подключения к pgAdmin, в формате email (login@email.net)regex=^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$; maxlength=255; minlength=3
171passwordstringПароль для подключения к pgAdmin
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
164domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$; unique_scope=provider
165resource_cpuinteger500Квота ядра пода (milicores). 1000Mili = 1 Ядроminvalue=500; maxvalue=4000
166resource_memoryinteger512Квота памяти пода (MBytes)minvalue=512; maxvalue=8192
167resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/pgadmin_params_modify.md b/generated/test/docs/pgadmin_params_modify.md new file mode 100644 index 0000000..28c4957 --- /dev/null +++ b/generated/test/docs/pgadmin_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_pgadmin + +Service ID: `96` + +Service Name: pgAdmin + +[Manual](pgadmin.md) | [Create params](pgadmin_params_create.md) | [Modify params](pgadmin_params_modify.md) | [Output params](pgadmin_outputs.md) | [Operations](pgadmin_ops.md) | [Example](pgadmin_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
172resource_cpuintegerКвота ядра пода (Указывается в mili). 1000Mili = 1 Ядроminvalue=500; maxvalue=4000
173resource_memoryintegerКвота памяти пода (MBytes)minvalue=512; maxvalue=8192
174resource_diskintegerКвота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
diff --git a/generated/test/docs/postgres.md b/generated/test/docs/postgres.md new file mode 100644 index 0000000..61e7668 --- /dev/null +++ b/generated/test/docs/postgres.md @@ -0,0 +1,13 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## MAN + +
+# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
+
diff --git a/generated/test/docs/postgres_backup.md b/generated/test/docs/postgres_backup.md new file mode 100644 index 0000000..96074ea --- /dev/null +++ b/generated/test/docs/postgres_backup.md @@ -0,0 +1,13 @@ +# Resource nubes_postgres_backup + +Service: `nubes_postgres` (ID: 90) + +[PostgreSQL (основной)](postgres.md) | [Operations](postgres_ops.md) | [Backup](postgres_backup.md) | [Example](postgres_backup_example.md) + +## Create params + +None. + +## Delete params + +None. diff --git a/generated/test/docs/postgres_backup_example.md b/generated/test/docs/postgres_backup_example.md new file mode 100644 index 0000000..a49493b --- /dev/null +++ b/generated/test/docs/postgres_backup_example.md @@ -0,0 +1,30 @@ +# Resource nubes_postgres_backup — Example + +Service: `nubes_postgres` (ID: 90) + +[PostgreSQL (основной)](postgres.md) | [Operations](postgres_ops.md) | [Backup](postgres_backup.md) | [Example](postgres_backup_example.md) + +## Copy-ready manifest (`postgres_backup_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_postgres должен быть создан заранее +# resource "nubes_postgres" "baza" { ... } + +resource "nubes_postgres_backup" "example" { + postgres_id = nubes_postgres.baza.id +} +``` diff --git a/generated/test/docs/postgres_database.md b/generated/test/docs/postgres_database.md new file mode 100644 index 0000000..af1dd78 --- /dev/null +++ b/generated/test/docs/postgres_database.md @@ -0,0 +1,24 @@ +# Resource nubes_postgres_database + +Service: `nubes_postgres` (ID: 90) + +[PostgreSQL (основной)](postgres.md) | [Operations](postgres_ops.md) | [Database](postgres_database.md) | [Example](postgres_database_example.md) + +## Create params + +**Обязательные параметры** + + + + + + +
IDCodeTypeDescriptionConstraints
741db_namestringregex=^[A-Za-z0-9]+$
742db_ownerstring
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
743db_namestring
diff --git a/generated/test/docs/postgres_database_example.md b/generated/test/docs/postgres_database_example.md new file mode 100644 index 0000000..083d2f8 --- /dev/null +++ b/generated/test/docs/postgres_database_example.md @@ -0,0 +1,32 @@ +# Resource nubes_postgres_database — Example + +Service: `nubes_postgres` (ID: 90) + +[PostgreSQL (основной)](postgres.md) | [Operations](postgres_ops.md) | [Database](postgres_database.md) | [Example](postgres_database_example.md) + +## Copy-ready manifest (`postgres_database_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_postgres должен быть создан заранее +# resource "nubes_postgres" "baza" { ... } + +resource "nubes_postgres_database" "example" { + postgres_id = nubes_postgres.baza.id + db_name = "TODO" + db_owner = "TODO" +} +``` diff --git a/generated/test/docs/postgres_example.md b/generated/test/docs/postgres_example.md new file mode 100644 index 0000000..961c1b8 --- /dev/null +++ b/generated/test/docs/postgres_example.md @@ -0,0 +1,48 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Copy-ready manifest (`postgres_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_postgres" "baza" { + cluster_configuration = "TODO" + startup_configuration = "TODO" + access_configuration = "TODO" + postgres_configuration = "TODO" + backup_configuration = "TODO" + autoscale_configuration = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_postgres.baza.state_params["имя_ключа"] +nubes_postgres.baza.state_out["имя_ключа"] +nubes_postgres.baza.state_params_flat["имя_ключа"] +nubes_postgres.baza.state_out_flat["имя_ключа"] +nubes_postgres.baza.vault_secrets["имя_ключа"] +nubes_postgres.baza.vault_url +nubes_postgres.baza.vault_user_path +nubes_postgres.baza.vault_fields +``` diff --git a/generated/test/docs/postgres_ops.md b/generated/test/docs/postgres_ops.md new file mode 100644 index 0000000..cc2703c --- /dev/null +++ b/generated/test/docs/postgres_ops.md @@ -0,0 +1,23 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](postgres_params_create.md) +- `create_backup` — . См. [nubes_postgres_backup](postgres_backup.md) +- `create_database` — . См. [nubes_postgres_database](postgres_database.md) +- `create_user` — . См. [nubes_postgres_user](postgres_user.md) +- `delete` +- `delete_database` — . См. [nubes_postgres_database](postgres_database.md) +- `delete_user` — . См. [nubes_postgres_user](postgres_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](postgres_params_modify.md) +- `reconcile` +- `recovery` +- `restart` — ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов. Может быть полная поломка. Крайне осторожно запускать при работе +- `resume` +- `suspend` diff --git a/generated/test/docs/postgres_outputs.md b/generated/test/docs/postgres_outputs.md new file mode 100644 index 0000000..f07b697 --- /dev/null +++ b/generated/test/docs/postgres_outputs.md @@ -0,0 +1,65 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.master"]` +- `state_out_flat["externalConnect.master.fqdn"]` +- `state_out_flat["externalConnect.master.ip"]` +- `state_out_flat["externalConnect.master.isExternal"]` +- `state_out_flat["externalConnect.master.uuid"]` +- `state_out_flat["externalConnect.slave"]` +- `state_out_flat["externalConnect.slave.fqdn"]` +- `state_out_flat["externalConnect.slave.ip"]` +- `state_out_flat["externalConnect.slave.isExternal"]` +- `state_out_flat["externalConnect.slave.uuid"]` +- `state_out_flat["internalConnect"]` +- `state_out_flat["internalConnect.master"]` +- `state_out_flat["internalConnect.slave"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.k8spostgresql"]` +- `state_out_flat["monitoring.k8spostgresql.url"]` +- `state_out_flat["monitoring.k8spostgresqlbackup"]` +- `state_out_flat["monitoring.k8spostgresqlbackup.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` +- `vault_secrets["standbyPass"]` +- `vault_secrets["standbyUser"]` + +### Пример для связки с Lucee/NodeJS + +```hcl +testds_connectionString = "jdbc:postgresql://${nubes_postgres.db2.state_out_flat["internalConnect.master"]}:5432/postgres?sslmode=require" +testds_username = nubes_postgres.db2.vault_secrets["adminUser"] +testds_password = nubes_postgres.db2.vault_secrets["adminPass"] +``` diff --git a/generated/test/docs/postgres_params.md b/generated/test/docs/postgres_params.md new file mode 100644 index 0000000..b44e9f4 --- /dev/null +++ b/generated/test/docs/postgres_params.md @@ -0,0 +1,12 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Parameters + +- [Create params](postgres_params_create.md) +- [Modify params](postgres_params_modify.md) diff --git a/generated/test/docs/postgres_params_create.md b/generated/test/docs/postgres_params_create.md new file mode 100644 index 0000000..8957e32 --- /dev/null +++ b/generated/test/docs/postgres_params_create.md @@ -0,0 +1,38 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
788cluster_configurationmap-fixed
789startup_configurationmap-fixed
790access_configurationmap-fixed
791postgres_configurationmap-fixed
792postgres_confarray-map-fixed
793backup_configurationmap-fixed
794autoscale_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/postgres_params_modify.md b/generated/test/docs/postgres_params_modify.md new file mode 100644 index 0000000..5a67a37 --- /dev/null +++ b/generated/test/docs/postgres_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_postgres + +Service ID: `90` + +Service Name: PostgreSQL + +[Manual](postgres.md) | [Create params](postgres_params_create.md) | [Modify params](postgres_params_modify.md) | [Output params](postgres_outputs.md) | [Operations](postgres_ops.md) | [Example](postgres_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
795cluster_configurationmap-fixed
796access_configurationmap-fixed
797postgres_configurationmap-fixed
798postgres_confarray-map-fixed
799backup_configurationmap-fixed
800autoscale_configurationmap-fixed
diff --git a/generated/test/docs/postgres_user.md b/generated/test/docs/postgres_user.md new file mode 100644 index 0000000..e82e589 --- /dev/null +++ b/generated/test/docs/postgres_user.md @@ -0,0 +1,24 @@ +# Resource nubes_postgres_user + +Service: `nubes_postgres` (ID: 90) + +[PostgreSQL (основной)](postgres.md) | [Operations](postgres_ops.md) | [User](postgres_user.md) | [Example](postgres_user_example.md) + +## Create params + +**Обязательные параметры** + + + + + + +
IDCodeTypeDescriptionConstraints
733usernamestringregex=^[A-Za-z0-9]+$; maxlength=62; minlength=2
740rolestring`app_user` — стандартный пользователь с базовым набором прав
`ddl_user` — пользователь с расширенными правами, включая управление ролями и базами данных
value_list=app_user, ddl_user
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
739usernamestringПример: `username-admin`
diff --git a/generated/test/docs/postgres_user_example.md b/generated/test/docs/postgres_user_example.md new file mode 100644 index 0000000..9aa11f2 --- /dev/null +++ b/generated/test/docs/postgres_user_example.md @@ -0,0 +1,32 @@ +# Resource nubes_postgres_user — Example + +Service: `nubes_postgres` (ID: 90) + +[PostgreSQL (основной)](postgres.md) | [Operations](postgres_ops.md) | [User](postgres_user.md) | [Example](postgres_user_example.md) + +## Copy-ready manifest (`postgres_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_postgres должен быть создан заранее +# resource "nubes_postgres" "baza" { ... } + +resource "nubes_postgres_user" "example" { + postgres_id = nubes_postgres.baza.id + username = "TODO" + role = "app_user" # Выбрать роль из списка +} +``` diff --git a/generated/test/docs/rabbitmq.md b/generated/test/docs/rabbitmq.md new file mode 100644 index 0000000..d0e5af3 --- /dev/null +++ b/generated/test/docs/rabbitmq.md @@ -0,0 +1,13 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## MAN + +
+# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

--- +
diff --git a/generated/test/docs/rabbitmq_example.md b/generated/test/docs/rabbitmq_example.md new file mode 100644 index 0000000..1c955b9 --- /dev/null +++ b/generated/test/docs/rabbitmq_example.md @@ -0,0 +1,50 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Copy-ready manifest (`rabbitmq_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_rabbitmq" "baza" { + resource_realm = "TODO" # Выбрать платформу из списка + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + resource_cpu = 1000 # Пример: `2000` + resource_memory = 1124 # Пример: `2048` + resource_disk = 30 # Пример: `20` + resource_instances = 1 # Пример: `1` + need_external_address_master = false # Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_rabbitmq.baza.state_params["имя_ключа"] +nubes_rabbitmq.baza.state_out["имя_ключа"] +nubes_rabbitmq.baza.state_params_flat["имя_ключа"] +nubes_rabbitmq.baza.state_out_flat["имя_ключа"] +nubes_rabbitmq.baza.vault_secrets["имя_ключа"] +nubes_rabbitmq.baza.vault_url +nubes_rabbitmq.baza.vault_user_path +nubes_rabbitmq.baza.vault_fields +``` diff --git a/generated/test/docs/rabbitmq_ops.md b/generated/test/docs/rabbitmq_ops.md new file mode 100644 index 0000000..fe63cf6 --- /dev/null +++ b/generated/test/docs/rabbitmq_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](rabbitmq_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](rabbitmq_params_modify.md) +- `resume` +- `suspend` — Пока не работает из за того что RabbitmqCluster нельзя занулить diff --git a/generated/test/docs/rabbitmq_outputs.md b/generated/test/docs/rabbitmq_outputs.md new file mode 100644 index 0000000..30d870e --- /dev/null +++ b/generated/test/docs/rabbitmq_outputs.md @@ -0,0 +1,48 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["clusterDomain"]` +- `state_out_flat["externalConnect"]` +- `state_out_flat["externalConnect.admin"]` +- `state_out_flat["externalConnect.admin.fqdn"]` +- `state_out_flat["externalConnect.admin.ip"]` +- `state_out_flat["externalConnect.admin.isExternal"]` +- `state_out_flat["externalConnect.admin.uuid"]` +- `state_out_flat["inernalConnect"]` +- `state_out_flat["inernalConnect.master"]` +- `state_out_flat["inernalConnect.slave"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.base"]` +- `state_out_flat["monitoring.base.url"]` +- `state_out_flat["monitoring.loki"]` +- `state_out_flat["monitoring.loki.url"]` + +### `vault_secrets` ключи + +- `vault_secrets["adminPass"]` +- `vault_secrets["adminUser"]` + diff --git a/generated/test/docs/rabbitmq_params.md b/generated/test/docs/rabbitmq_params.md new file mode 100644 index 0000000..b743d18 --- /dev/null +++ b/generated/test/docs/rabbitmq_params.md @@ -0,0 +1,12 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Parameters + +- [Create params](rabbitmq_params_create.md) +- [Modify params](rabbitmq_params_modify.md) diff --git a/generated/test/docs/rabbitmq_params_create.md b/generated/test/docs/rabbitmq_params_create.md new file mode 100644 index 0000000..1a1b21f --- /dev/null +++ b/generated/test/docs/rabbitmq_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
119resource_realmstringВыбрать платформу из спискаfunc=getAvailableResourceRealms
543ip_space_name_masterstringПример: `internet-no-antiddos-v1`
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
116resource_cpuinteger1000Пример: `2000`minvalue=500; maxvalue=4000
117resource_memoryinteger1124Пример: `2048`minvalue=512; maxvalue=8192
118resource_diskinteger30Пример: `20`minvalue=5; maxvalue=999
224resource_instancesinteger1Пример: `1`minvalue=1; maxvalue=7
225need_external_address_masterbooleanfalseФлаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`value_list=false, true
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/rabbitmq_params_modify.md b/generated/test/docs/rabbitmq_params_modify.md new file mode 100644 index 0000000..6df624c --- /dev/null +++ b/generated/test/docs/rabbitmq_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_rabbitmq + +Service ID: `93` + +Service Name: RabbitMQ + +[Manual](rabbitmq.md) | [Create params](rabbitmq_params_create.md) | [Modify params](rabbitmq_params_modify.md) | [Output params](rabbitmq_outputs.md) | [Operations](rabbitmq_ops.md) | [Example](rabbitmq_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
304resource_cpuintegerПример: `2000`minvalue=500; maxvalue=4000
305resource_memoryintegerПример: `2048`minvalue=512; maxvalue=8192
306resource_diskintegerПример: `20`minvalue=1; maxvalue=999
307resource_instancesintegerПример: `1`minvalue=1; maxvalue=7
308need_external_address_masterbooleanВыделяет внешний (белый) IP для доступа к mastervalue_list=false, true
545ip_space_name_masterstringПример: `internet-no-antiddos-v1`
diff --git a/generated/test/docs/redis.md b/generated/test/docs/redis.md new file mode 100644 index 0000000..4dfd2ea --- /dev/null +++ b/generated/test/docs/redis.md @@ -0,0 +1,13 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## MAN + +
+Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379 +
diff --git a/generated/test/docs/redis_example.md b/generated/test/docs/redis_example.md new file mode 100644 index 0000000..559e7f4 --- /dev/null +++ b/generated/test/docs/redis_example.md @@ -0,0 +1,45 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Copy-ready manifest (`redis_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_redis" "baza" { + startup_configuration = "TODO" + cluster_configuration = "TODO" + access_configuration = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_redis.baza.state_params["имя_ключа"] +nubes_redis.baza.state_out["имя_ключа"] +nubes_redis.baza.state_params_flat["имя_ключа"] +nubes_redis.baza.state_out_flat["имя_ключа"] +nubes_redis.baza.vault_secrets["имя_ключа"] +nubes_redis.baza.vault_url +nubes_redis.baza.vault_user_path +nubes_redis.baza.vault_fields +``` diff --git a/generated/test/docs/redis_ops.md b/generated/test/docs/redis_ops.md new file mode 100644 index 0000000..4156679 --- /dev/null +++ b/generated/test/docs/redis_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](redis_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](redis_params_modify.md) +- `reconcile` +- `resume` +- `suspend` diff --git a/generated/test/docs/redis_outputs.md b/generated/test/docs/redis_outputs.md new file mode 100644 index 0000000..c735f67 --- /dev/null +++ b/generated/test/docs/redis_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/redis_params.md b/generated/test/docs/redis_params.md new file mode 100644 index 0000000..131dd5e --- /dev/null +++ b/generated/test/docs/redis_params.md @@ -0,0 +1,12 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Parameters + +- [Create params](redis_params_create.md) +- [Modify params](redis_params_modify.md) diff --git a/generated/test/docs/redis_params_create.md b/generated/test/docs/redis_params_create.md new file mode 100644 index 0000000..2eff875 --- /dev/null +++ b/generated/test/docs/redis_params_create.md @@ -0,0 +1,34 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
1061startup_configurationmap-fixed
1062cluster_configurationmap-fixed
1063access_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/redis_params_modify.md b/generated/test/docs/redis_params_modify.md new file mode 100644 index 0000000..b4ed8a0 --- /dev/null +++ b/generated/test/docs/redis_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_redis + +Service ID: `91` + +Service Name: Redis + +[Manual](redis.md) | [Create params](redis_params_create.md) | [Modify params](redis_params_modify.md) | [Output params](redis_outputs.md) | [Operations](redis_ops.md) | [Example](redis_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
1064cluster_configurationmap-fixed
1065access_configurationmap-fixed
diff --git a/generated/test/docs/s3.md b/generated/test/docs/s3.md new file mode 100644 index 0000000..15a87a0 --- /dev/null +++ b/generated/test/docs/s3.md @@ -0,0 +1,13 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## MAN + +
+Руководство доступно по ссылке:
https://docs.s3.msk-1.ngcloud.ru/s3/s3.description.html +
diff --git a/generated/test/docs/s3_example.md b/generated/test/docs/s3_example.md new file mode 100644 index 0000000..2c54570 --- /dev/null +++ b/generated/test/docs/s3_example.md @@ -0,0 +1,44 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Copy-ready manifest (`s3_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_s3" "baza" { + resource_realm = "TODO" # Платформа для развертывания + display_name = "TODO" # Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_s3.baza.state_params["имя_ключа"] +nubes_s3.baza.state_out["имя_ключа"] +nubes_s3.baza.state_params_flat["имя_ключа"] +nubes_s3.baza.state_out_flat["имя_ключа"] +nubes_s3.baza.vault_secrets["имя_ключа"] +nubes_s3.baza.vault_url +nubes_s3.baza.vault_user_path +nubes_s3.baza.vault_fields +``` diff --git a/generated/test/docs/s3_ops.md b/generated/test/docs/s3_ops.md new file mode 100644 index 0000000..655b982 --- /dev/null +++ b/generated/test/docs/s3_ops.md @@ -0,0 +1,18 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](s3_params_create.md) +- `create_sub_user` — . См. [nubes_s3_sub_user](s3_sub_user.md) +- `delete` +- `delete_sub_user` — . См. [nubes_s3_sub_user](s3_sub_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](s3_params_modify.md) +- `reconcile` +- `resume` +- `suspend` diff --git a/generated/test/docs/s3_outputs.md b/generated/test/docs/s3_outputs.md new file mode 100644 index 0000000..e1cdeb2 --- /dev/null +++ b/generated/test/docs/s3_outputs.md @@ -0,0 +1,42 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["dtEndTrial"]` +- `state_out_flat["dtStartTrial"]` +- `state_out_flat["isTrial"]` +- `state_out_flat["monitoring"]` +- `state_out_flat["monitoring.allDashboards"]` +- `state_out_flat["monitoring.s3ceph"]` +- `state_out_flat["monitoring.s3ceph.url"]` +- `state_out_flat["urlConnect"]` + +### `vault_secrets` ключи + +- `vault_secrets["1325_access_key"]` +- `vault_secrets["1325_secret_key"]` +- `vault_secrets["1325_technicalS3Backup_access_key"]` +- `vault_secrets["1325_technicalS3Backup_secret_key"]` + diff --git a/generated/test/docs/s3_params.md b/generated/test/docs/s3_params.md new file mode 100644 index 0000000..d61aced --- /dev/null +++ b/generated/test/docs/s3_params.md @@ -0,0 +1,12 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Parameters + +- [Create params](s3_params_create.md) +- [Modify params](s3_params_modify.md) diff --git a/generated/test/docs/s3_params_create.md b/generated/test/docs/s3_params_create.md new file mode 100644 index 0000000..b9a9364 --- /dev/null +++ b/generated/test/docs/s3_params_create.md @@ -0,0 +1,33 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
163resource_realmкластер S3 для подключенияfunc=getAvailableResourceRealms
320display_namestringЧеловеко-читаемое имя (транслит из Elma) - передаётся имя клиентаregex=^[a-zA-Z0-9_]{1,61}[a-zA-Z0-9]$
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/s3_params_modify.md b/generated/test/docs/s3_params_modify.md new file mode 100644 index 0000000..d5a1da0 --- /dev/null +++ b/generated/test/docs/s3_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_s3 + +Service ID: `12` + +Service Name: S3 Object Storage + +[Manual](s3.md) | [Create params](s3_params_create.md) | [Modify params](s3_params_modify.md) | [Output params](s3_outputs.md) | [Operations](s3_ops.md) | [Example](s3_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
50max_size_gb_per_userintegermaxSizeGb. Передаём рукамиminvalue=1; maxvalue=50000
51max_objects_per_bucketintegerКвота на бакет. Кол-во объектов. Передаём рукамиminvalue=1; maxvalue=10000000
52max_buckets_per_userintegerКвота на пользователя. Количество бакетов пользователя. Передаём рукамиminvalue=1; maxvalue=1000
diff --git a/generated/test/docs/s3_sub_user.md b/generated/test/docs/s3_sub_user.md new file mode 100644 index 0000000..1975010 --- /dev/null +++ b/generated/test/docs/s3_sub_user.md @@ -0,0 +1,24 @@ +# Resource nubes_s3_sub_user + +Service: `nubes_s3` (ID: 12) + +[S3 Object Storage (основной)](s3.md) | [Operations](s3_ops.md) | [Sub_user](s3_sub_user.md) | [Example](s3_sub_user_example.md) + +## Create params + +**Обязательные параметры** + + + + + + +
IDCodeTypeDescriptionConstraints
60sub_user_namestring
61sub_user_accessstringvalue_list=read, write, readwrite, full
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
63sub_user_namestringsubUserName. По идее услуга уже создана - должен передаваться из ЛК
diff --git a/generated/test/docs/s3_sub_user_example.md b/generated/test/docs/s3_sub_user_example.md new file mode 100644 index 0000000..b0634b7 --- /dev/null +++ b/generated/test/docs/s3_sub_user_example.md @@ -0,0 +1,32 @@ +# Resource nubes_s3_sub_user — Example + +Service: `nubes_s3` (ID: 12) + +[S3 Object Storage (основной)](s3.md) | [Operations](s3_ops.md) | [Sub_user](s3_sub_user.md) | [Example](s3_sub_user_example.md) + +## Copy-ready manifest (`s3_sub_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_s3 должен быть создан заранее +# resource "nubes_s3" "baza" { ... } + +resource "nubes_s3_sub_user" "example" { + s3_id = nubes_s3.baza.id + sub_user_name = "TODO" + sub_user_access = "read" +} +``` diff --git a/generated/test/docs/s3bucket.md b/generated/test/docs/s3bucket.md new file mode 100644 index 0000000..27d17d7 --- /dev/null +++ b/generated/test/docs/s3bucket.md @@ -0,0 +1,13 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/s3bucket_example.md b/generated/test/docs/s3bucket_example.md new file mode 100644 index 0000000..7233482 --- /dev/null +++ b/generated/test/docs/s3bucket_example.md @@ -0,0 +1,51 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Copy-ready manifest (`s3bucket_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_s3bucket" "baza" { + s3_user_uid = "TODO" # UID Корневой услуги S3. На основе него будет создан бакет. + bucket_name = "TODO" # Имя бакета s3 + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + max_size = -1 # Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes + read_all = false # Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. + list_all = false # Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. + cors_all = false # Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS + placement = "HOT" # Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_s3bucket.baza.state_params["имя_ключа"] +nubes_s3bucket.baza.state_out["имя_ключа"] +nubes_s3bucket.baza.state_params_flat["имя_ключа"] +nubes_s3bucket.baza.state_out_flat["имя_ключа"] +nubes_s3bucket.baza.vault_secrets["имя_ключа"] +nubes_s3bucket.baza.vault_url +nubes_s3bucket.baza.vault_user_path +nubes_s3bucket.baza.vault_fields +``` diff --git a/generated/test/docs/s3bucket_ops.md b/generated/test/docs/s3bucket_ops.md new file mode 100644 index 0000000..61b2abb --- /dev/null +++ b/generated/test/docs/s3bucket_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](s3bucket_params_create.md) +- `delete` diff --git a/generated/test/docs/s3bucket_outputs.md b/generated/test/docs/s3bucket_outputs.md new file mode 100644 index 0000000..f62a754 --- /dev/null +++ b/generated/test/docs/s3bucket_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["httpConnect"]` + diff --git a/generated/test/docs/s3bucket_params.md b/generated/test/docs/s3bucket_params.md new file mode 100644 index 0000000..9977e26 --- /dev/null +++ b/generated/test/docs/s3bucket_params.md @@ -0,0 +1,12 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Parameters + +- [Create params](s3bucket_params_create.md) +- [Modify params](s3bucket_params_modify.md) diff --git a/generated/test/docs/s3bucket_params_create.md b/generated/test/docs/s3bucket_params_create.md new file mode 100644 index 0000000..0ae6f44 --- /dev/null +++ b/generated/test/docs/s3bucket_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
124s3_user_uiduuidUID Корневой услуги S3. На основе него будет создан бакет.ref_svc_id=12
125bucket_namestringИмя бакета s3
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
126max_size-1Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes
127read_allbooleanfalseПубличный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета.value_list=false, true
128list_allbooleanfalseПубличный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете.value_list=false, true
129cors_allbooleanfalseПубличный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORSvalue_list=false, true
130placementstringHOTКласс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращенияvalue_list=HOT, COLD
diff --git a/generated/test/docs/s3bucket_params_modify.md b/generated/test/docs/s3bucket_params_modify.md new file mode 100644 index 0000000..ca24765 --- /dev/null +++ b/generated/test/docs/s3bucket_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_s3bucket + +Service ID: `13` + +Service Name: S3 бакет + +[Manual](s3bucket.md) | [Create params](s3bucket_params_create.md) | [Modify params](s3bucket_params_modify.md) | [Output params](s3bucket_outputs.md) | [Operations](s3bucket_ops.md) | [Example](s3bucket_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/superset.md b/generated/test/docs/superset.md new file mode 100644 index 0000000..93094e9 --- /dev/null +++ b/generated/test/docs/superset.md @@ -0,0 +1,13 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## MAN + +
+Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru) +
diff --git a/generated/test/docs/superset_example.md b/generated/test/docs/superset_example.md new file mode 100644 index 0000000..13aa0c4 --- /dev/null +++ b/generated/test/docs/superset_example.md @@ -0,0 +1,51 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Copy-ready manifest (`superset_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_superset" "baza" { + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + domain = "web01" # Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + emails = "testuser1" # Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел + resource_cpu = 500 # Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро + resource_memory = 512 # Квота памяти пода (Указывается в Megabytes) + resource_disk = 1 # Квота диска (Указывается в GIGAbytes) + resource_instances = 1 # Количество узлов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_superset.baza.state_params["имя_ключа"] +nubes_superset.baza.state_out["имя_ключа"] +nubes_superset.baza.state_params_flat["имя_ключа"] +nubes_superset.baza.state_out_flat["имя_ключа"] +nubes_superset.baza.vault_secrets["имя_ключа"] +nubes_superset.baza.vault_url +nubes_superset.baza.vault_user_path +nubes_superset.baza.vault_fields +``` diff --git a/generated/test/docs/superset_ops.md b/generated/test/docs/superset_ops.md new file mode 100644 index 0000000..5aa441d --- /dev/null +++ b/generated/test/docs/superset_ops.md @@ -0,0 +1,12 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](superset_params_create.md) +- `delete` — DEV-Ветка удаления Superset diff --git a/generated/test/docs/superset_outputs.md b/generated/test/docs/superset_outputs.md new file mode 100644 index 0000000..4d7a1fd --- /dev/null +++ b/generated/test/docs/superset_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/superset_params.md b/generated/test/docs/superset_params.md new file mode 100644 index 0000000..2984f1c --- /dev/null +++ b/generated/test/docs/superset_params.md @@ -0,0 +1,12 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Parameters + +- [Create params](superset_params_create.md) +- [Modify params](superset_params_modify.md) diff --git a/generated/test/docs/superset_params_create.md b/generated/test/docs/superset_params_create.md new file mode 100644 index 0000000..6652cf1 --- /dev/null +++ b/generated/test/docs/superset_params_create.md @@ -0,0 +1,29 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
109resource_realmstringНаименование кластера Kubernetes для подключенияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
14domainstringweb01Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealmregex=^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$
20emailsstringtestuser1Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел
96resource_cpuinteger500Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядроminvalue=100; maxvalue=4000
97resource_memoryinteger512Квота памяти пода (Указывается в Megabytes)minvalue=128; maxvalue=8192
98resource_diskinteger1Квота диска (Указывается в GIGAbytes)minvalue=1; maxvalue=999
227resource_instancesinteger1Количество узловminvalue=1; maxvalue=7
diff --git a/generated/test/docs/superset_params_modify.md b/generated/test/docs/superset_params_modify.md new file mode 100644 index 0000000..5a6e1ca --- /dev/null +++ b/generated/test/docs/superset_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_superset + +Service ID: `81` + +Service Name: Apache Superset + +[Manual](superset.md) | [Create params](superset_params_create.md) | [Modify params](superset_params_modify.md) | [Output params](superset_outputs.md) | [Operations](superset_ops.md) | [Example](superset_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/template.md b/generated/test/docs/template.md new file mode 100644 index 0000000..b4d0dc3 --- /dev/null +++ b/generated/test/docs/template.md @@ -0,0 +1,13 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/template_example.md b/generated/test/docs/template_example.md new file mode 100644 index 0000000..d2719b9 --- /dev/null +++ b/generated/test/docs/template_example.md @@ -0,0 +1,45 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Copy-ready manifest (`template_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_template" "baza" { + cluster_configuration = "TODO" + startup_configuration = "TODO" + access_configuration = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_template.baza.state_params["имя_ключа"] +nubes_template.baza.state_out["имя_ключа"] +nubes_template.baza.state_params_flat["имя_ключа"] +nubes_template.baza.state_out_flat["имя_ключа"] +nubes_template.baza.vault_secrets["имя_ключа"] +nubes_template.baza.vault_url +nubes_template.baza.vault_user_path +nubes_template.baza.vault_fields +``` diff --git a/generated/test/docs/template_ops.md b/generated/test/docs/template_ops.md new file mode 100644 index 0000000..f431055 --- /dev/null +++ b/generated/test/docs/template_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](template_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](template_params_modify.md) +- `reconcile` +- `resume` +- `suspend` diff --git a/generated/test/docs/template_outputs.md b/generated/test/docs/template_outputs.md new file mode 100644 index 0000000..ab5623d --- /dev/null +++ b/generated/test/docs/template_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/template_params.md b/generated/test/docs/template_params.md new file mode 100644 index 0000000..7fdd8d3 --- /dev/null +++ b/generated/test/docs/template_params.md @@ -0,0 +1,12 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Parameters + +- [Create params](template_params_create.md) +- [Modify params](template_params_modify.md) diff --git a/generated/test/docs/template_params_create.md b/generated/test/docs/template_params_create.md new file mode 100644 index 0000000..6266f53 --- /dev/null +++ b/generated/test/docs/template_params_create.md @@ -0,0 +1,34 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
813cluster_configurationmap-fixed
814startup_configurationmap-fixed
815access_configurationmap-fixed
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/template_params_modify.md b/generated/test/docs/template_params_modify.md new file mode 100644 index 0000000..4611040 --- /dev/null +++ b/generated/test/docs/template_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_template + +Service ID: `2` + +Service Name: Темплейт k8s + +[Manual](template.md) | [Create params](template_params_create.md) | [Modify params](template_params_modify.md) | [Output params](template_outputs.md) | [Operations](template_ops.md) | [Example](template_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
816cluster_configurationmap-fixed
817access_configurationmap-fixed
diff --git a/generated/test/docs/tenant.md b/generated/test/docs/tenant.md new file mode 100644 index 0000000..409820a --- /dev/null +++ b/generated/test/docs/tenant.md @@ -0,0 +1,13 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/tenant_example.md b/generated/test/docs/tenant_example.md new file mode 100644 index 0000000..7e50ae6 --- /dev/null +++ b/generated/test/docs/tenant_example.md @@ -0,0 +1,44 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Copy-ready manifest (`tenant_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_tenant" "baza" { + grafana_org_name = "TODO" # WZ-Код клиента + resource_realm = "TODO" # Платформа для развертывания +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_tenant.baza.state_params["имя_ключа"] +nubes_tenant.baza.state_out["имя_ключа"] +nubes_tenant.baza.state_params_flat["имя_ключа"] +nubes_tenant.baza.state_out_flat["имя_ключа"] +nubes_tenant.baza.vault_secrets["имя_ключа"] +nubes_tenant.baza.vault_url +nubes_tenant.baza.vault_user_path +nubes_tenant.baza.vault_fields +``` diff --git a/generated/test/docs/tenant_ops.md b/generated/test/docs/tenant_ops.md new file mode 100644 index 0000000..5058136 --- /dev/null +++ b/generated/test/docs/tenant_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](tenant_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](tenant_params_modify.md) diff --git a/generated/test/docs/tenant_outputs.md b/generated/test/docs/tenant_outputs.md new file mode 100644 index 0000000..4ebe277 --- /dev/null +++ b/generated/test/docs/tenant_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/tenant_params.md b/generated/test/docs/tenant_params.md new file mode 100644 index 0000000..4db7d6b --- /dev/null +++ b/generated/test/docs/tenant_params.md @@ -0,0 +1,12 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Parameters + +- [Create params](tenant_params_create.md) +- [Modify params](tenant_params_modify.md) diff --git a/generated/test/docs/tenant_params_create.md b/generated/test/docs/tenant_params_create.md new file mode 100644 index 0000000..10d4d27 --- /dev/null +++ b/generated/test/docs/tenant_params_create.md @@ -0,0 +1,21 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
239grafana_org_namestringWZ-Код клиентаregex=^WZ[\d]+$
240resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/test/docs/tenant_params_modify.md b/generated/test/docs/tenant_params_modify.md new file mode 100644 index 0000000..cc3e107 --- /dev/null +++ b/generated/test/docs/tenant_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_tenant + +Service ID: `112` + +Service Name: Тенант в Grafana + +[Manual](tenant.md) | [Create params](tenant_params_create.md) | [Modify params](tenant_params_modify.md) | [Output params](tenant_outputs.md) | [Operations](tenant_ops.md) | [Example](tenant_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/valo_tenant.md b/generated/test/docs/valo_tenant.md new file mode 100644 index 0000000..6860e2f --- /dev/null +++ b/generated/test/docs/valo_tenant.md @@ -0,0 +1,13 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## MAN + +
+# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
+
diff --git a/generated/test/docs/valo_tenant_example.md b/generated/test/docs/valo_tenant_example.md new file mode 100644 index 0000000..0982a12 --- /dev/null +++ b/generated/test/docs/valo_tenant_example.md @@ -0,0 +1,48 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Copy-ready manifest (`valo_tenant_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_valo_tenant" "baza" { + resource_realm = "TODO" + domain = "TODO" # Будет добавлено в зону valo.ngcloud.ru + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + valo_options = {} + s3_storage = 10 # Параметр указывается в `Gb` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_valo_tenant.baza.state_params["имя_ключа"] +nubes_valo_tenant.baza.state_out["имя_ключа"] +nubes_valo_tenant.baza.state_params_flat["имя_ключа"] +nubes_valo_tenant.baza.state_out_flat["имя_ключа"] +nubes_valo_tenant.baza.vault_secrets["имя_ключа"] +nubes_valo_tenant.baza.vault_url +nubes_valo_tenant.baza.vault_user_path +nubes_valo_tenant.baza.vault_fields +``` diff --git a/generated/test/docs/valo_tenant_ops.md b/generated/test/docs/valo_tenant_ops.md new file mode 100644 index 0000000..89cb168 --- /dev/null +++ b/generated/test/docs/valo_tenant_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](valo_tenant_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](valo_tenant_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/test/docs/valo_tenant_outputs.md b/generated/test/docs/valo_tenant_outputs.md new file mode 100644 index 0000000..2d4ea5d --- /dev/null +++ b/generated/test/docs/valo_tenant_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/valo_tenant_params.md b/generated/test/docs/valo_tenant_params.md new file mode 100644 index 0000000..2c3d14f --- /dev/null +++ b/generated/test/docs/valo_tenant_params.md @@ -0,0 +1,12 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Parameters + +- [Create params](valo_tenant_params_create.md) +- [Modify params](valo_tenant_params_modify.md) diff --git a/generated/test/docs/valo_tenant_params_create.md b/generated/test/docs/valo_tenant_params_create.md new file mode 100644 index 0000000..a0e9538 --- /dev/null +++ b/generated/test/docs/valo_tenant_params_create.md @@ -0,0 +1,38 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
655resource_realmstringfunc=getAvailableResourceRealms
656domainstringБудет добавлено в зону valo.ngcloud.ruregex=^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$; maxlength=61; minlength=3
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
657valo_optionsmap-fixed{}
658s3_storageinteger10Данные хранятся в отдельном экземпляре S3minvalue=1; maxvalue=999
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/valo_tenant_params_modify.md b/generated/test/docs/valo_tenant_params_modify.md new file mode 100644 index 0000000..4b07bec --- /dev/null +++ b/generated/test/docs/valo_tenant_params_modify.md @@ -0,0 +1,16 @@ +# Resource nubes_valo_tenant + +Service ID: `149` + +Service Name: VALO Cloud + +[Manual](valo_tenant.md) | [Create params](valo_tenant_params_create.md) | [Modify params](valo_tenant_params_modify.md) | [Output params](valo_tenant_outputs.md) | [Operations](valo_tenant_ops.md) | [Example](valo_tenant_example.md) + +## Modify params + + + + + + +
IDCodeTypeDescriptionConstraints
659valo_optionsmap-fixed
660s3_storageintegerДанные хранятся в отдельном экземпляре S3
diff --git a/generated/test/docs/vapp.md b/generated/test/docs/vapp.md new file mode 100644 index 0000000..630f47c --- /dev/null +++ b/generated/test/docs/vapp.md @@ -0,0 +1,13 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## MAN + +
+# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции. +
diff --git a/generated/test/docs/vapp_example.md b/generated/test/docs/vapp_example.md new file mode 100644 index 0000000..a8f08a7 --- /dev/null +++ b/generated/test/docs/vapp_example.md @@ -0,0 +1,45 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Copy-ready manifest (`vapp_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vapp" "baza" { + nsxt_uid = "TODO" # UUID Услуги `Сетевой шлюз периметра (Edge)` + vapp_name = "TODO" # Уникальное в рамках организации + vdc_uid = "TODO" # UUID Услуги `Виртуальный датацентр (vDC)` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vapp.baza.state_params["имя_ключа"] +nubes_vapp.baza.state_out["имя_ключа"] +nubes_vapp.baza.state_params_flat["имя_ключа"] +nubes_vapp.baza.state_out_flat["имя_ключа"] +nubes_vapp.baza.vault_secrets["имя_ключа"] +nubes_vapp.baza.vault_url +nubes_vapp.baza.vault_user_path +nubes_vapp.baza.vault_fields +``` diff --git a/generated/test/docs/vapp_ops.md b/generated/test/docs/vapp_ops.md new file mode 100644 index 0000000..b787780 --- /dev/null +++ b/generated/test/docs/vapp_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vapp_params_create.md) +- `delete` +- `reconcile` +- `resume` +- `suspend` diff --git a/generated/test/docs/vapp_outputs.md b/generated/test/docs/vapp_outputs.md new file mode 100644 index 0000000..0ff0f8e --- /dev/null +++ b/generated/test/docs/vapp_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vapp_params.md b/generated/test/docs/vapp_params.md new file mode 100644 index 0000000..4c7addc --- /dev/null +++ b/generated/test/docs/vapp_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Parameters + +- [Create params](vapp_params_create.md) +- [Modify params](vapp_params_modify.md) diff --git a/generated/test/docs/vapp_params_create.md b/generated/test/docs/vapp_params_create.md new file mode 100644 index 0000000..3b23cff --- /dev/null +++ b/generated/test/docs/vapp_params_create.md @@ -0,0 +1,34 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
190nsxt_uiduuidК какой сети может быть подключен виртуальный каталогref_svc_id=22
191vapp_namestringИмеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин)
regex=^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$; minlength=3
623vdc_uiduuidВ каких ресурсах будет создан виртуальный каталогref_svc_id=21
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/vapp_params_modify.md b/generated/test/docs/vapp_params_modify.md new file mode 100644 index 0000000..4fca747 --- /dev/null +++ b/generated/test/docs/vapp_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vapp + +Service ID: `26` + +Service Name: Виртуальный каталог ВМ (vApp) + +[Manual](vapp.md) | [Create params](vapp_params_create.md) | [Modify params](vapp_params_modify.md) | [Output params](vapp_outputs.md) | [Operations](vapp_ops.md) | [Example](vapp_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/vc_complex.md b/generated/test/docs/vc_complex.md new file mode 100644 index 0000000..c83cece --- /dev/null +++ b/generated/test/docs/vc_complex.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## MAN + +
+# Создает окружение для ВМ.

## Создает сервисы:
**Организация**
**Виртуальный датацентр**
**Каталог**
**Сетевой шлюз периметра**

+
diff --git a/generated/test/docs/vc_complex_example.md b/generated/test/docs/vc_complex_example.md new file mode 100644 index 0000000..5228d80 --- /dev/null +++ b/generated/test/docs/vc_complex_example.md @@ -0,0 +1,59 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Copy-ready manifest (`vc_complex_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_complex" "baza" { + resource_realm = "TODO" # Платформа для развертывания + vapp_name = "TODO" # Имя vApp + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + vdc_provider_gateway = "sandbox-v1cl1-pvdc" # Взять из свойств ресурсной платформы. Путь .vcd.providerVdc - взять KEY providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String + virtual_services_count = 1 # Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 + ip_space_name = "internet-ipv4-v1" # Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT + storage_config = [{"name": "", "size": 1024}] # Описывается как массив объектов, где описывается имя политики и её размер + vdc_network_pool = "nsxt-sandbox-geneve-np" + cpu_guaranteed = 20 + mem_guaranteed = 20 + segroup_name = "SEGROUP-SANDBOX-CL1-SHARED-01" # Наименование segroup, в котором будут выделяться пулы на AVI + need_external_address_snat = true # Выделяет внешний IP на Edge. Используется для SNAT всего трафика + need_enable_avi = false # Включает AVI в Cloud Director + cpu_allocated = 1 # шт + mem_allocated = 10 # gb + v_ipcount = 0 # ш +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_complex.baza.state_params["имя_ключа"] +nubes_vc_complex.baza.state_out["имя_ключа"] +nubes_vc_complex.baza.state_params_flat["имя_ключа"] +nubes_vc_complex.baza.state_out_flat["имя_ключа"] +nubes_vc_complex.baza.vault_secrets["имя_ключа"] +nubes_vc_complex.baza.vault_url +nubes_vc_complex.baza.vault_user_path +nubes_vc_complex.baza.vault_fields +``` diff --git a/generated/test/docs/vc_complex_ops.md b/generated/test/docs/vc_complex_ops.md new file mode 100644 index 0000000..9d016bc --- /dev/null +++ b/generated/test/docs/vc_complex_ops.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_complex_params_create.md) diff --git a/generated/test/docs/vc_complex_outputs.md b/generated/test/docs/vc_complex_outputs.md new file mode 100644 index 0000000..629a059 --- /dev/null +++ b/generated/test/docs/vc_complex_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vc_complex_params.md b/generated/test/docs/vc_complex_params.md new file mode 100644 index 0000000..82b3c13 --- /dev/null +++ b/generated/test/docs/vc_complex_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Parameters + +- [Create params](vc_complex_params_create.md) +- [Modify params](vc_complex_params_modify.md) diff --git a/generated/test/docs/vc_complex_params_create.md b/generated/test/docs/vc_complex_params_create.md new file mode 100644 index 0000000..6b0b9ec --- /dev/null +++ b/generated/test/docs/vc_complex_params_create.md @@ -0,0 +1,37 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
332resource_realmstringНаименование ресурсной платформыfunc=getAvailableResourceRealms
334vapp_namestringИмя vAppregex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
358vdc_provider_gatewaystringsandbox-v1cl1-pvdcВзять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List&lt;String&gt;) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String
359virtual_services_count1Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4minvalue=1; maxvalue=4
360ip_space_namestringinternet-ipv4-v1Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT
399storage_configjson[{"name": "", "size": 1024}]Описывается как массив объектов, где описывается имя политики и её размер
400vdc_network_poolstringnsxt-sandbox-geneve-np
401cpu_guaranteedinteger20value_list=20, 50, 80
402mem_guaranteedinteger20value_list=20, 50, 80
403segroup_namestringSEGROUP-SANDBOX-CL1-SHARED-01Наименование segroup, в котором будут выделяться пулы на AVI
404need_external_address_snatbooleantrueВыделяет внешний IP на Edge. Используется для SNAT всего трафикаvalue_list=false, true
405need_enable_avibooleanfalseВключает AVI в Cloud Directorvalue_list=false, true
566cpu_allocatedstring1штminvalue=1
567mem_allocatedinteger10gbminvalue=1
568v_ipcountinteger0шminvalue=0; maxvalue=20
diff --git a/generated/test/docs/vc_complex_params_modify.md b/generated/test/docs/vc_complex_params_modify.md new file mode 100644 index 0000000..b6a1be3 --- /dev/null +++ b/generated/test/docs/vc_complex_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_complex + +Service ID: `113` + +Service Name: Быстрый старт + +[Manual](vc_complex.md) | [Create params](vc_complex_params_create.md) | [Modify params](vc_complex_params_modify.md) | [Output params](vc_complex_outputs.md) | [Operations](vc_complex_ops.md) | [Example](vc_complex_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/vc_nsxt.md b/generated/test/docs/vc_nsxt.md new file mode 100644 index 0000000..0c8056d --- /dev/null +++ b/generated/test/docs/vc_nsxt.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## MAN + +
+# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

+
diff --git a/generated/test/docs/vc_nsxt_example.md b/generated/test/docs/vc_nsxt_example.md new file mode 100644 index 0000000..1ca1eb2 --- /dev/null +++ b/generated/test/docs/vc_nsxt_example.md @@ -0,0 +1,47 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Copy-ready manifest (`vc_nsxt_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_nsxt" "baza" { + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + need_enable_avi = false # Включение Load Balancer + virtual_services_count = 1 # Кол-во виртуальных сервисов, которые **резервируются** на AVI + vdc_type = "vdc" # Тип родительской услуги +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_nsxt.baza.state_params["имя_ключа"] +nubes_vc_nsxt.baza.state_out["имя_ключа"] +nubes_vc_nsxt.baza.state_params_flat["имя_ключа"] +nubes_vc_nsxt.baza.state_out_flat["имя_ключа"] +nubes_vc_nsxt.baza.vault_secrets["имя_ключа"] +nubes_vc_nsxt.baza.vault_url +nubes_vc_nsxt.baza.vault_user_path +nubes_vc_nsxt.baza.vault_fields +``` diff --git a/generated/test/docs/vc_nsxt_ops.md b/generated/test/docs/vc_nsxt_ops.md new file mode 100644 index 0000000..03af84f --- /dev/null +++ b/generated/test/docs/vc_nsxt_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_nsxt_params_create.md) +- `delete` — Невозможно удалить если routed-сеть в Edge связана с: - vApp - VM - Kubernetes-кластер Штурвал Запустить операцию возможно, но будет ошибка +- `modify` — Изменение кластера. Параметры: [Modify params](vc_nsxt_params_modify.md) +- `reconcile` diff --git a/generated/test/docs/vc_nsxt_outputs.md b/generated/test/docs/vc_nsxt_outputs.md new file mode 100644 index 0000000..717cbc4 --- /dev/null +++ b/generated/test/docs/vc_nsxt_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vc_nsxt_params.md b/generated/test/docs/vc_nsxt_params.md new file mode 100644 index 0000000..2cf1cc1 --- /dev/null +++ b/generated/test/docs/vc_nsxt_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Parameters + +- [Create params](vc_nsxt_params_create.md) +- [Modify params](vc_nsxt_params_modify.md) diff --git a/generated/test/docs/vc_nsxt_params_create.md b/generated/test/docs/vc_nsxt_params_create.md new file mode 100644 index 0000000..90c487e --- /dev/null +++ b/generated/test/docs/vc_nsxt_params_create.md @@ -0,0 +1,28 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
8vdc_uidstringНеобходимо, если выбран тип подключаемой VDC: `vdc`ref_svc_id=21
622vdc_group_uidstringНеобходимо, если выбран тип подключаемой VDC: `groupvdc`ref_svc_id=29
825qos_profileif (vdcUid != '&quot;) {
наборфункций1
}

elif (vdcGroupUid != &quot;&quot;) {
наборфункций2
}

else {
return &quot;Необходимо выбрать vdc или vdcgroup&quot;
}
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
340need_enable_avibooleanfalseПараметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указатьvalue_list=false, true
341virtual_services_countinteger1Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB`
minvalue=1; maxvalue=4
621vdc_typestringvdcПри выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc
value_list=vdc, vdcGroup
diff --git a/generated/test/docs/vc_nsxt_params_modify.md b/generated/test/docs/vc_nsxt_params_modify.md new file mode 100644 index 0000000..0030cd9 --- /dev/null +++ b/generated/test/docs/vc_nsxt_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_nsxt + +Service ID: `22` + +Service Name: Сетевой шлюз периметра (Edge) + +[Manual](vc_nsxt.md) | [Create params](vc_nsxt_params_create.md) | [Modify params](vc_nsxt_params_modify.md) | [Output params](vc_nsxt_outputs.md) | [Operations](vc_nsxt_ops.md) | [Example](vc_nsxt_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
368need_enable_avibooleanПараметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указатьvalue_list=false, true
369virtual_services_countintegerВо избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB`
minvalue=1; maxvalue=4
372ip_space_namestringНеобходимо указывать, если включён параметр `Выделить VIP для SNAT`
856qos_profilestring
diff --git a/generated/test/docs/vc_org.md b/generated/test/docs/vc_org.md new file mode 100644 index 0000000..e500201 --- /dev/null +++ b/generated/test/docs/vc_org.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## MAN + +
+# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak. +
diff --git a/generated/test/docs/vc_org_example.md b/generated/test/docs/vc_org_example.md new file mode 100644 index 0000000..5bae91c --- /dev/null +++ b/generated/test/docs/vc_org_example.md @@ -0,0 +1,46 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Copy-ready manifest (`vc_org_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_org" "baza" { + resource_realm = "TODO" # Облако в котором будет развёрнут тенант + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + organization_type = "iaas" # Доступ к организации предоставляется только при выборе `iaas` +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_org.baza.state_params["имя_ключа"] +nubes_vc_org.baza.state_out["имя_ключа"] +nubes_vc_org.baza.state_params_flat["имя_ключа"] +nubes_vc_org.baza.state_out_flat["имя_ключа"] +nubes_vc_org.baza.vault_secrets["имя_ключа"] +nubes_vc_org.baza.vault_url +nubes_vc_org.baza.vault_user_path +nubes_vc_org.baza.vault_fields +``` diff --git a/generated/test/docs/vc_org_ops.md b/generated/test/docs/vc_org_ops.md new file mode 100644 index 0000000..aebdbcb --- /dev/null +++ b/generated/test/docs/vc_org_ops.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_org_params_create.md) +- `create_user` — . См. [nubes_vc_org_user](vc_org_user.md) +- `delete` — Удалить возможно только через 14 дней после выполнения операции `suspend` **ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта** Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов +- `delete_user` — . См. [nubes_vc_org_user](vc_org_user.md) +- `modify` — Изменение кластера. Параметры: [Modify params](vc_org_params_modify.md) +- `reconcile` +- `resume` +- `suspend` — Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта diff --git a/generated/test/docs/vc_org_outputs.md b/generated/test/docs/vc_org_outputs.md new file mode 100644 index 0000000..9de11b9 --- /dev/null +++ b/generated/test/docs/vc_org_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["urlConnect"]` + diff --git a/generated/test/docs/vc_org_params.md b/generated/test/docs/vc_org_params.md new file mode 100644 index 0000000..e187b07 --- /dev/null +++ b/generated/test/docs/vc_org_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Parameters + +- [Create params](vc_org_params_create.md) +- [Modify params](vc_org_params_modify.md) diff --git a/generated/test/docs/vc_org_params_create.md b/generated/test/docs/vc_org_params_create.md new file mode 100644 index 0000000..28f12f0 --- /dev/null +++ b/generated/test/docs/vc_org_params_create.md @@ -0,0 +1,36 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
418resource_realmstringВыбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы
func=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
556organization_typestringiaasДоступ к организации предоставляется только при выборе `iaas`value_list=iaas, saas
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/vc_org_params_modify.md b/generated/test/docs/vc_org_params_modify.md new file mode 100644 index 0000000..4142f72 --- /dev/null +++ b/generated/test/docs/vc_org_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_org + +Service ID: `19` + +Service Name: Организация в Cloud Director + +[Manual](vc_org.md) | [Create params](vc_org_params_create.md) | [Modify params](vc_org_params_modify.md) | [Output params](vc_org_outputs.md) | [Operations](vc_org_ops.md) | [Example](vc_org_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
662v_ipconfigurearray-map-fixed
diff --git a/generated/test/docs/vc_org_saas.md b/generated/test/docs/vc_org_saas.md new file mode 100644 index 0000000..95ae27c --- /dev/null +++ b/generated/test/docs/vc_org_saas.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## MAN + +
+Верхнеуровневая сущность создаваемая для Заказчика.
Внутри Организация вы можете создать: Виртуальные датацентры, Каталоги. +
diff --git a/generated/test/docs/vc_org_saas_example.md b/generated/test/docs/vc_org_saas_example.md new file mode 100644 index 0000000..c460211 --- /dev/null +++ b/generated/test/docs/vc_org_saas_example.md @@ -0,0 +1,43 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Copy-ready manifest (`vc_org_saas_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_org_saas" "baza" { + resource_realm = "TODO" # Платформа для развертывания +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_org_saas.baza.state_params["имя_ключа"] +nubes_vc_org_saas.baza.state_out["имя_ключа"] +nubes_vc_org_saas.baza.state_params_flat["имя_ключа"] +nubes_vc_org_saas.baza.state_out_flat["имя_ключа"] +nubes_vc_org_saas.baza.vault_secrets["имя_ключа"] +nubes_vc_org_saas.baza.vault_url +nubes_vc_org_saas.baza.vault_user_path +nubes_vc_org_saas.baza.vault_fields +``` diff --git a/generated/test/docs/vc_org_saas_ops.md b/generated/test/docs/vc_org_saas_ops.md new file mode 100644 index 0000000..bcc4618 --- /dev/null +++ b/generated/test/docs/vc_org_saas_ops.md @@ -0,0 +1,14 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_org_saas_params_create.md) +- `delete` +- `resume` +- `suspend` diff --git a/generated/test/docs/vc_org_saas_outputs.md b/generated/test/docs/vc_org_saas_outputs.md new file mode 100644 index 0000000..15052c9 --- /dev/null +++ b/generated/test/docs/vc_org_saas_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vc_org_saas_params.md b/generated/test/docs/vc_org_saas_params.md new file mode 100644 index 0000000..a9c08f3 --- /dev/null +++ b/generated/test/docs/vc_org_saas_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Parameters + +- [Create params](vc_org_saas_params_create.md) +- [Modify params](vc_org_saas_params_modify.md) diff --git a/generated/test/docs/vc_org_saas_params_create.md b/generated/test/docs/vc_org_saas_params_create.md new file mode 100644 index 0000000..d73866d --- /dev/null +++ b/generated/test/docs/vc_org_saas_params_create.md @@ -0,0 +1,32 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
185resource_realmstringНаименование ресурсной платформыfunc=getAvailableResourceRealms
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. + +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/vc_org_saas_params_modify.md b/generated/test/docs/vc_org_saas_params_modify.md new file mode 100644 index 0000000..ad45c99 --- /dev/null +++ b/generated/test/docs/vc_org_saas_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_org_saas + +Service ID: `20` + +Service Name: Организация [DEPRECATED] + +[Manual](vc_org_saas.md) | [Create params](vc_org_saas_params_create.md) | [Modify params](vc_org_saas_params_modify.md) | [Output params](vc_org_saas_outputs.md) | [Operations](vc_org_saas_ops.md) | [Example](vc_org_saas_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/vc_org_user.md b/generated/test/docs/vc_org_user.md new file mode 100644 index 0000000..10b4e0f --- /dev/null +++ b/generated/test/docs/vc_org_user.md @@ -0,0 +1,27 @@ +# Resource nubes_vc_org_user + +Service: `nubes_vc_org` (ID: 19) + +[Организация в Cloud Director (основной)](vc_org.md) | [Operations](vc_org_ops.md) | [User](vc_org_user.md) | [Example](vc_org_user_example.md) + +## Create params + +**Обязательные параметры** + +None. + +**Параметры со значением по умолчанию** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
871adminsarray-map-fixed[{}]
+ +## Delete params + + + + + +
IDCodeTypeDescriptionConstraints
872adminsarray-map-fixed
diff --git a/generated/test/docs/vc_org_user_example.md b/generated/test/docs/vc_org_user_example.md new file mode 100644 index 0000000..6acd8bd --- /dev/null +++ b/generated/test/docs/vc_org_user_example.md @@ -0,0 +1,33 @@ +# Resource nubes_vc_org_user — Example + +Service: `nubes_vc_org` (ID: 19) + +[Организация в Cloud Director (основной)](vc_org.md) | [Operations](vc_org_ops.md) | [User](vc_org_user.md) | [Example](vc_org_user_example.md) + +## Copy-ready manifest (`vc_org_user_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_vc_org должен быть создан заранее +# resource "nubes_vc_org" "baza" { ... } + +resource "nubes_vc_org_user" "example" { + vc_org_id = nubes_vc_org.baza.id + + # Параметры со значением по умолчанию + admins = [{}] +} +``` diff --git a/generated/test/docs/vc_vdc.md b/generated/test/docs/vc_vdc.md new file mode 100644 index 0000000..2d8fa57 --- /dev/null +++ b/generated/test/docs/vc_vdc.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## MAN + +
+# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
+
diff --git a/generated/test/docs/vc_vdc_example.md b/generated/test/docs/vc_vdc_example.md new file mode 100644 index 0000000..5eaf99a --- /dev/null +++ b/generated/test/docs/vc_vdc_example.md @@ -0,0 +1,51 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Copy-ready manifest (`vc_vdc_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_vdc" "baza" { + organization_uid = "TODO" # Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` + provider_vdc = "TODO" # Приписка `2.8` - Скорость vCPU - 2800MHz И так далее... + network_provider = "TODO" + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + storage_config = [{}] # Первая политика в списке указывается как "По умолчанию" + cpu_guaranteed = 20 # % резервирования + cpu_allocated = 80 # шт + mem_allocated = 200 # gb +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vdc.baza.state_params["имя_ключа"] +nubes_vc_vdc.baza.state_out["имя_ключа"] +nubes_vc_vdc.baza.state_params_flat["имя_ключа"] +nubes_vc_vdc.baza.state_out_flat["имя_ключа"] +nubes_vc_vdc.baza.vault_secrets["имя_ключа"] +nubes_vc_vdc.baza.vault_url +nubes_vc_vdc.baza.vault_user_path +nubes_vc_vdc.baza.vault_fields +``` diff --git a/generated/test/docs/vc_vdc_group.md b/generated/test/docs/vc_vdc_group.md new file mode 100644 index 0000000..1ff3cd6 --- /dev/null +++ b/generated/test/docs/vc_vdc_group.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## MAN + +
+# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc` +
diff --git a/generated/test/docs/vc_vdc_group_example.md b/generated/test/docs/vc_vdc_group_example.md new file mode 100644 index 0000000..a60fbff --- /dev/null +++ b/generated/test/docs/vc_vdc_group_example.md @@ -0,0 +1,43 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Copy-ready manifest (`vc_vdc_group_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_vdc_group" "baza" { + vdc_uid = "TODO" +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vdc_group.baza.state_params["имя_ключа"] +nubes_vc_vdc_group.baza.state_out["имя_ключа"] +nubes_vc_vdc_group.baza.state_params_flat["имя_ключа"] +nubes_vc_vdc_group.baza.state_out_flat["имя_ключа"] +nubes_vc_vdc_group.baza.vault_secrets["имя_ключа"] +nubes_vc_vdc_group.baza.vault_url +nubes_vc_vdc_group.baza.vault_user_path +nubes_vc_vdc_group.baza.vault_fields +``` diff --git a/generated/test/docs/vc_vdc_group_ops.md b/generated/test/docs/vc_vdc_group_ops.md new file mode 100644 index 0000000..ed3ebe7 --- /dev/null +++ b/generated/test/docs/vc_vdc_group_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Operations + +- `add_vdc` — **Не нужно** выбирать UUID VDC, который уже был добавлен в группу. См. [nubes_vc_vdc_group_vdc](vc_vdc_group_vdc.md) +- `create` — Создание кластера. Параметры: [Create params](vc_vdc_group_params_create.md) +- `delete` +- `reconcile` +- `remove_vdc` — . См. [nubes_vc_vdc_group_vdc](vc_vdc_group_vdc.md) diff --git a/generated/test/docs/vc_vdc_group_outputs.md b/generated/test/docs/vc_vdc_group_outputs.md new file mode 100644 index 0000000..5714cde --- /dev/null +++ b/generated/test/docs/vc_vdc_group_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vc_vdc_group_params.md b/generated/test/docs/vc_vdc_group_params.md new file mode 100644 index 0000000..d292ab9 --- /dev/null +++ b/generated/test/docs/vc_vdc_group_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Parameters + +- [Create params](vc_vdc_group_params_create.md) +- [Modify params](vc_vdc_group_params_modify.md) diff --git a/generated/test/docs/vc_vdc_group_params_create.md b/generated/test/docs/vc_vdc_group_params_create.md new file mode 100644 index 0000000..624e982 --- /dev/null +++ b/generated/test/docs/vc_vdc_group_params_create.md @@ -0,0 +1,20 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + +
IDCodeTypeDescriptionConstraints
618vdc_uiduuidref_svc_id=21
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/test/docs/vc_vdc_group_params_modify.md b/generated/test/docs/vc_vdc_group_params_modify.md new file mode 100644 index 0000000..721de57 --- /dev/null +++ b/generated/test/docs/vc_vdc_group_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vc_vdc_group + +Service ID: `29` + +Service Name: Группа датацентров + +[Manual](vc_vdc_group.md) | [Create params](vc_vdc_group_params_create.md) | [Modify params](vc_vdc_group_params_modify.md) | [Output params](vc_vdc_group_outputs.md) | [Operations](vc_vdc_group_ops.md) | [Example](vc_vdc_group_example.md) + +## Modify params + +None. diff --git a/generated/test/docs/vc_vdc_group_vdc.md b/generated/test/docs/vc_vdc_group_vdc.md new file mode 100644 index 0000000..0bf40c0 --- /dev/null +++ b/generated/test/docs/vc_vdc_group_vdc.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vdc_group_vdc + +Service: `nubes_vc_vdc_group` (ID: 29) + +[Группа датацентров (основной)](vc_vdc_group.md) | [Operations](vc_vdc_group_ops.md) | [Vdc](vc_vdc_group_vdc.md) | [Example](vc_vdc_group_vdc_example.md) + +## Create params + +None. + +## Delete params + +None. diff --git a/generated/test/docs/vc_vdc_group_vdc_example.md b/generated/test/docs/vc_vdc_group_vdc_example.md new file mode 100644 index 0000000..83b0d9e --- /dev/null +++ b/generated/test/docs/vc_vdc_group_vdc_example.md @@ -0,0 +1,30 @@ +# Resource nubes_vc_vdc_group_vdc — Example + +Service: `nubes_vc_vdc_group` (ID: 29) + +[Группа датацентров (основной)](vc_vdc_group.md) | [Operations](vc_vdc_group_ops.md) | [Vdc](vc_vdc_group_vdc.md) | [Example](vc_vdc_group_vdc_example.md) + +## Copy-ready manifest (`vc_vdc_group_vdc_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} + +# Родительский ресурс — nubes_vc_vdc_group должен быть создан заранее +# resource "nubes_vc_vdc_group" "baza" { ... } + +resource "nubes_vc_vdc_group_vdc" "example" { + vc_vdc_group_id = nubes_vc_vdc_group.baza.id +} +``` diff --git a/generated/test/docs/vc_vdc_ops.md b/generated/test/docs/vc_vdc_ops.md new file mode 100644 index 0000000..b1a1776 --- /dev/null +++ b/generated/test/docs/vc_vdc_ops.md @@ -0,0 +1,16 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vdc_params_create.md) +- `delete` — Удалить возможно только через 14 дней после выполнения операции `suspend` **ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC** Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vdc_params_modify.md) +- `reconcile` +- `resume` +- `suspend` — Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC diff --git a/generated/test/docs/vc_vdc_outputs.md b/generated/test/docs/vc_vdc_outputs.md new file mode 100644 index 0000000..a435292 --- /dev/null +++ b/generated/test/docs/vc_vdc_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["vdcName"]` + diff --git a/generated/test/docs/vc_vdc_params.md b/generated/test/docs/vc_vdc_params.md new file mode 100644 index 0000000..03c0037 --- /dev/null +++ b/generated/test/docs/vc_vdc_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Parameters + +- [Create params](vc_vdc_params_create.md) +- [Modify params](vc_vdc_params_modify.md) diff --git a/generated/test/docs/vc_vdc_params_create.md b/generated/test/docs/vc_vdc_params_create.md new file mode 100644 index 0000000..5529ac4 --- /dev/null +++ b/generated/test/docs/vc_vdc_params_create.md @@ -0,0 +1,41 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + +
IDCodeTypeDescriptionConstraints
30organization_uiduuidДля создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director`ref_svc_id=19
335provider_vdcstringПриписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
746network_providerstring
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
361storage_configarray-map-fixed[{}]Первая политика в списке указывается как &quot;По умолчанию&quot;
397cpu_guaranteedinteger20Свойство нельзя изменить после созданияvalue_list=20, 50, 80
557cpu_allocatedinteger80штminvalue=1; maxvalue=1000
558mem_allocatedinteger200gbminvalue=1; maxvalue=4096
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/vc_vdc_params_modify.md b/generated/test/docs/vc_vdc_params_modify.md new file mode 100644 index 0000000..4bb0906 --- /dev/null +++ b/generated/test/docs/vc_vdc_params_modify.md @@ -0,0 +1,17 @@ +# Resource nubes_vc_vdc + +Service ID: `21` + +Service Name: Виртуальный датацентр (vDC) + +[Manual](vc_vdc.md) | [Create params](vc_vdc_params_create.md) | [Modify params](vc_vdc_params_modify.md) | [Output params](vc_vdc_outputs.md) | [Operations](vc_vdc_ops.md) | [Example](vc_vdc_example.md) + +## Modify params + + + + + + + +
IDCodeTypeDescriptionConstraints
560cpu_allocatedintegerштminvalue=1; maxvalue=999
561mem_allocatedintegergbminvalue=1; maxvalue=1024
562storage_configarray-map-fixedПервая политика в списке указывается как &quot;По умолчанию&quot;
diff --git a/generated/test/docs/vc_vm.md b/generated/test/docs/vc_vm.md new file mode 100644 index 0000000..e0c84ef --- /dev/null +++ b/generated/test/docs/vc_vm.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## MAN + +
+Виртуальная машина без каких-либо особенностей.
Данная услуга запускает vc_ip для выдачи белого адреса и подключения к ВМ +
diff --git a/generated/test/docs/vc_vm_example.md b/generated/test/docs/vc_vm_example.md new file mode 100644 index 0000000..c05d7fe --- /dev/null +++ b/generated/test/docs/vc_vm_example.md @@ -0,0 +1,52 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Copy-ready manifest (`vc_vm_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_vm" "baza" { + vapp_uid = "TODO" # InstanceUid vApp. vApp должен быть создан + ip_space_name = "TODO" # Брать из свойств ресурсной платформы .vcd.ipSpace Имя IP-space для выделения внешнего IP (IP выделяется обязательно) + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + vm_name = "vm0001" # Имя ВМ. Не поддерживает спец-символы + vm_cpu = 2 # CPU + vm_ram = 2 # RAM (GB) + vm_disksize = 10 # Размер диска под сервис (GB) + template_name = "RockyLinux_9-16G" # ОС + access_ip_list = "79.111.119.78/32 185.247.187.1/32" # Вставить содержимое из 'curl ifconfig.io' IP адреса в CIDR формате через пробел для настройки аксес листов +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vm.baza.state_params["имя_ключа"] +nubes_vc_vm.baza.state_out["имя_ключа"] +nubes_vc_vm.baza.state_params_flat["имя_ключа"] +nubes_vc_vm.baza.state_out_flat["имя_ключа"] +nubes_vc_vm.baza.vault_secrets["имя_ключа"] +nubes_vc_vm.baza.vault_url +nubes_vc_vm.baza.vault_user_path +nubes_vc_vm.baza.vault_fields +``` diff --git a/generated/test/docs/vc_vm_ops.md b/generated/test/docs/vc_vm_ops.md new file mode 100644 index 0000000..ff1e0cf --- /dev/null +++ b/generated/test/docs/vc_vm_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vm_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vm_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/test/docs/vc_vm_outputs.md b/generated/test/docs/vc_vm_outputs.md new file mode 100644 index 0000000..d5163ba --- /dev/null +++ b/generated/test/docs/vc_vm_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vc_vm_params.md b/generated/test/docs/vc_vm_params.md new file mode 100644 index 0000000..0ff6c3e --- /dev/null +++ b/generated/test/docs/vc_vm_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Parameters + +- [Create params](vc_vm_params_create.md) +- [Modify params](vc_vm_params_modify.md) diff --git a/generated/test/docs/vc_vm_params_create.md b/generated/test/docs/vc_vm_params_create.md new file mode 100644 index 0000000..5bed0ba --- /dev/null +++ b/generated/test/docs/vc_vm_params_create.md @@ -0,0 +1,42 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + +
IDCodeTypeDescriptionConstraints
33vapp_uiduuidInstanceUid vApp. vApp должен быть созданref_svc_id=26
406ip_space_namestringБрать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно)
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
35vm_namestringvm0001Имя ВМ. Не поддерживает спец-символыregex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
36vm_cpustring2CPU
37vm_ramstring2RAM (GB)
38vm_disksizestring10Размер диска под сервис (GB)
176template_namestringRockyLinux_9-16GОСvalue_list=RockyLinux_9-16G, OracleLinux_9-16G, Ubuntu_24-20G, Ubuntu_22-20G
177access_ip_liststring79.111.119.78/32 185.247.187.1/32IP адреса в CIDR формате через пробел для настройки аксес листов
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/vc_vm_params_modify.md b/generated/test/docs/vc_vm_params_modify.md new file mode 100644 index 0000000..0ec4505 --- /dev/null +++ b/generated/test/docs/vc_vm_params_modify.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vm + +Service ID: `23` + +Service Name: VM в Cloud Director (старый формат) (vc_vm) + +[Manual](vc_vm.md) | [Create params](vc_vm_params_create.md) | [Modify params](vc_vm_params_modify.md) | [Output params](vc_vm_outputs.md) | [Operations](vc_vm_ops.md) | [Example](vc_vm_example.md) + +## Modify params + + + + + + + + +
IDCodeTypeDescriptionConstraints
186vm_cpuintegerCPU
187vm_ramintegerRAM
188vm_disksizeintegerРазмер диска под сервис (GB)
189access_ip_liststringIP адреса в CIDR формате через пробел для настройки аксес листов
diff --git a/generated/test/docs/vc_vm_v2.md b/generated/test/docs/vc_vm_v2.md new file mode 100644 index 0000000..ada3402 --- /dev/null +++ b/generated/test/docs/vc_vm_v2.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## MAN + +
+Manual not available. +
diff --git a/generated/test/docs/vc_vm_v2_example.md b/generated/test/docs/vc_vm_v2_example.md new file mode 100644 index 0000000..2902c17 --- /dev/null +++ b/generated/test/docs/vc_vm_v2_example.md @@ -0,0 +1,45 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Copy-ready manifest (`vc_vm_v2_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_vm_v2" "baza" { + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + server_list_json = [{}] # Необходимо изменить vappUid и ipSpaceName Пример конфигурации взять по ссылке ниже Объект серверов вида [{}] (Map(Object)) Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vm_v2.baza.state_params["имя_ключа"] +nubes_vc_vm_v2.baza.state_out["имя_ключа"] +nubes_vc_vm_v2.baza.state_params_flat["имя_ключа"] +nubes_vc_vm_v2.baza.state_out_flat["имя_ключа"] +nubes_vc_vm_v2.baza.vault_secrets["имя_ключа"] +nubes_vc_vm_v2.baza.vault_url +nubes_vc_vm_v2.baza.vault_user_path +nubes_vc_vm_v2.baza.vault_fields +``` diff --git a/generated/test/docs/vc_vm_v2_ops.md b/generated/test/docs/vc_vm_v2_ops.md new file mode 100644 index 0000000..9d940af --- /dev/null +++ b/generated/test/docs/vc_vm_v2_ops.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vm_v2_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vm_v2_params_modify.md) +- `resume` +- `suspend` diff --git a/generated/test/docs/vc_vm_v2_outputs.md b/generated/test/docs/vc_vm_v2_outputs.md new file mode 100644 index 0000000..3b9bace --- /dev/null +++ b/generated/test/docs/vc_vm_v2_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vc_vm_v2_params.md b/generated/test/docs/vc_vm_v2_params.md new file mode 100644 index 0000000..e60c762 --- /dev/null +++ b/generated/test/docs/vc_vm_v2_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Parameters + +- [Create params](vc_vm_v2_params_create.md) +- [Modify params](vc_vm_v2_params_modify.md) diff --git a/generated/test/docs/vc_vm_v2_params_create.md b/generated/test/docs/vc_vm_v2_params_create.md new file mode 100644 index 0000000..b1c20cc --- /dev/null +++ b/generated/test/docs/vc_vm_v2_params_create.md @@ -0,0 +1,32 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + +None. + +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + +
IDCodeTypeDefaultDescriptionConstraints
301server_list_jsonjson[{}]Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)
maxlength=65536; minlength=10
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/vc_vm_v2_params_modify.md b/generated/test/docs/vc_vm_v2_params_modify.md new file mode 100644 index 0000000..eedbec8 --- /dev/null +++ b/generated/test/docs/vc_vm_v2_params_modify.md @@ -0,0 +1,15 @@ +# Resource nubes_vc_vm_v2 + +Service ID: `27` + +Service Name: VM в Cloud Director (vc_vmV2) + +[Manual](vc_vm_v2.md) | [Create params](vc_vm_v2_params_create.md) | [Modify params](vc_vm_v2_params_modify.md) | [Output params](vc_vm_v2_outputs.md) | [Operations](vc_vm_v2_ops.md) | [Example](vc_vm_v2_example.md) + +## Modify params + + + + + +
IDCodeTypeDescriptionConstraints
302server_list_jsonjsonОбъект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)
maxlength=65536; minlength=10
diff --git a/generated/test/docs/vc_vm_v3.md b/generated/test/docs/vc_vm_v3.md new file mode 100644 index 0000000..95856d8 --- /dev/null +++ b/generated/test/docs/vc_vm_v3.md @@ -0,0 +1,13 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## MAN + +
+# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики +
diff --git a/generated/test/docs/vc_vm_v3_example.md b/generated/test/docs/vc_vm_v3_example.md new file mode 100644 index 0000000..4b5d17a --- /dev/null +++ b/generated/test/docs/vc_vm_v3_example.md @@ -0,0 +1,53 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Copy-ready manifest (`vc_vm_v3_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vc_vm_v3" "baza" { + vapp_uid = "TODO" + ip_space_name = "TODO" + image_vm = "TODO" + user_public_key = "TODO" # Публичная часть SSH-Ключа для пользователя + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + vm_name = "web01" + vm_cpu = 1 # шт + vm_ram = 1 # Gb + user_login = "myuser" # Учётка SSH + access_port_list = ["22"] # По каким портам открыть доступ к ВМ. Порты 22, 10050 проставляются автоматически Пока доступно: 22 - SSH, 80 - HTTP, 443 - HTTPS, 10050 - ZABBIX +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vc_vm_v3.baza.state_params["имя_ключа"] +nubes_vc_vm_v3.baza.state_out["имя_ключа"] +nubes_vc_vm_v3.baza.state_params_flat["имя_ключа"] +nubes_vc_vm_v3.baza.state_out_flat["имя_ключа"] +nubes_vc_vm_v3.baza.vault_secrets["имя_ключа"] +nubes_vc_vm_v3.baza.vault_url +nubes_vc_vm_v3.baza.vault_user_path +nubes_vc_vm_v3.baza.vault_fields +``` diff --git a/generated/test/docs/vc_vm_v3_ops.md b/generated/test/docs/vc_vm_v3_ops.md new file mode 100644 index 0000000..db02218 --- /dev/null +++ b/generated/test/docs/vc_vm_v3_ops.md @@ -0,0 +1,17 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vc_vm_v3_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vc_vm_v3_params_modify.md) +- `reconcile` +- `redeploy` +- `resume` +- `suspend` diff --git a/generated/test/docs/vc_vm_v3_outputs.md b/generated/test/docs/vc_vm_v3_outputs.md new file mode 100644 index 0000000..d0ef2e1 --- /dev/null +++ b/generated/test/docs/vc_vm_v3_outputs.md @@ -0,0 +1,30 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalConnect"]` +- `state_out_flat["fqdn"]` +- `state_out_flat["internalConnect"]` + diff --git a/generated/test/docs/vc_vm_v3_params.md b/generated/test/docs/vc_vm_v3_params.md new file mode 100644 index 0000000..9c63ad1 --- /dev/null +++ b/generated/test/docs/vc_vm_v3_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Parameters + +- [Create params](vc_vm_v3_params_create.md) +- [Modify params](vc_vm_v3_params_modify.md) diff --git a/generated/test/docs/vc_vm_v3_params_create.md b/generated/test/docs/vc_vm_v3_params_create.md new file mode 100644 index 0000000..aff77f9 --- /dev/null +++ b/generated/test/docs/vc_vm_v3_params_create.md @@ -0,0 +1,46 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
407vapp_uiduuidref_svc_id=26
411vm_diskintegerОсновной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму
regex=^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$
412ip_space_namestring
413access_ip_listjsonФормат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено [&quot;0.0.0.0/0&quot;]
regex=^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$
414image_vmstring
415cloud_inityamlМожно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов.
417user_public_keystringПубличная часть SSH-Ключа для пользователя
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + + + + +
IDCodeTypeDefaultDescriptionConstraints
408vm_namestringweb01regex=^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$
409vm_cpuinteger1штminvalue=1; maxvalue=64
410vm_raminteger1Gbminvalue=1; maxvalue=256
416user_loginmyuserУчётка SSH
448access_port_liststring["22"]По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX
+ +
+Параметры поведения (кратко)
+По умолчанию: `suspend_on_destroy = True`, `adopt_existing_on_create = False`.
+При `terraform destroy` или удалении ресурса из манифеста:
+- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
+- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
+При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
+- `false` — если ресурс уже есть, будет ошибка.
+- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
+Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления. +
diff --git a/generated/test/docs/vc_vm_v3_params_modify.md b/generated/test/docs/vc_vm_v3_params_modify.md new file mode 100644 index 0000000..e993928 --- /dev/null +++ b/generated/test/docs/vc_vm_v3_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_vc_vm_v3 + +Service ID: `28` + +Service Name: Виртуальная машина + +[Manual](vc_vm_v3.md) | [Create params](vc_vm_v3_params_create.md) | [Modify params](vc_vm_v3_params_modify.md) | [Output params](vc_vm_v3_outputs.md) | [Operations](vc_vm_v3_ops.md) | [Example](vc_vm_v3_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
493vm_cpuintegerштminvalue=1; maxvalue=64
494vm_ramintegerGbminvalue=1; maxvalue=256
495vm_diskintegerОсновной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму
regex=^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$
496ip_space_namestringno-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev
value_list=no-needed, internet-antiddos-v1, internet-no-antiddos-v1, internet-ipv4-v1
497access_ip_listjsonФормат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено [&quot;0.0.0.0/0&quot;]
498access_port_listjsonФормат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX
diff --git a/generated/test/docs/vcexternalip.md b/generated/test/docs/vcexternalip.md new file mode 100644 index 0000000..3fe625c --- /dev/null +++ b/generated/test/docs/vcexternalip.md @@ -0,0 +1,13 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## MAN + +
+**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса. +
diff --git a/generated/test/docs/vcexternalip_example.md b/generated/test/docs/vcexternalip_example.md new file mode 100644 index 0000000..54ce0b3 --- /dev/null +++ b/generated/test/docs/vcexternalip_example.md @@ -0,0 +1,51 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Copy-ready manifest (`vcexternalip_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vcexternalip" "baza" { + service_uid = "TODO" + internal_addr_access = "TODO" # Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true + from_service_namespace = "TODO" # Namespace составной услуги (для отображения правил FW) + ip_space_name = "TODO" # Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) + resource_realm = "TODO" # Платформа для развертывания + + # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте + dnat_create = true # Необходимо создать DNAT-правило. + internal_port_access = 5432 # Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vcexternalip.baza.state_params["имя_ключа"] +nubes_vcexternalip.baza.state_out["имя_ключа"] +nubes_vcexternalip.baza.state_params_flat["имя_ключа"] +nubes_vcexternalip.baza.state_out_flat["имя_ключа"] +nubes_vcexternalip.baza.vault_secrets["имя_ключа"] +nubes_vcexternalip.baza.vault_url +nubes_vcexternalip.baza.vault_user_path +nubes_vcexternalip.baza.vault_fields +``` diff --git a/generated/test/docs/vcexternalip_ops.md b/generated/test/docs/vcexternalip_ops.md new file mode 100644 index 0000000..5668481 --- /dev/null +++ b/generated/test/docs/vcexternalip_ops.md @@ -0,0 +1,13 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Operations + +- `create` — Создание кластера. Параметры: [Create params](vcexternalip_params_create.md) +- `delete` +- `modify` — Изменение кластера. Параметры: [Modify params](vcexternalip_params_modify.md) diff --git a/generated/test/docs/vcexternalip_outputs.md b/generated/test/docs/vcexternalip_outputs.md new file mode 100644 index 0000000..aaa59e1 --- /dev/null +++ b/generated/test/docs/vcexternalip_outputs.md @@ -0,0 +1,28 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов + +## Реальные поля из облака (running/suspended snapshot) + +Поля ниже получены из реальных инстансов этого сервиса в облаке. +Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`. + +### `state_out_flat` ключи + +- `state_out_flat["externalIp"]` + diff --git a/generated/test/docs/vcexternalip_params.md b/generated/test/docs/vcexternalip_params.md new file mode 100644 index 0000000..1db2fcc --- /dev/null +++ b/generated/test/docs/vcexternalip_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Parameters + +- [Create params](vcexternalip_params_create.md) +- [Modify params](vcexternalip_params_modify.md) diff --git a/generated/test/docs/vcexternalip_params_create.md b/generated/test/docs/vcexternalip_params_create.md new file mode 100644 index 0000000..995a3b8 --- /dev/null +++ b/generated/test/docs/vcexternalip_params_create.md @@ -0,0 +1,36 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + + + + + + + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
140service_uidstring
143snat_createbooleanНеобходимо создать SNAT-правило.
144internal_addr_accessstringВнутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true
324from_service_namespacestringNamespace составной услуги (для отображения правил FW)
325from_service_cloud_edge_namestringEdge составной услуги (указывать, если resourceRealm связан с CloudDirector)
326from_service_cloud_vdc_nameVdc составной услуги (указывать, если resourceRealm связан с CloudDirector)
327from_service_cloud_org_namestringOrg составной услуги (указывать, если resourceRealm связан с CloudDirector)
328from_service_cloud_vmware_urlstringApi-Url составной услуги (указывать, если resourceRealm связан с CloudDirector)
336ip_space_namestringТип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)
488resource_realmstringПлатформа для развертыванияfunc=getAvailableResourceRealms
624from_service_cloud_edge_scopestring
626from_service_vdc_group_namestring
+ +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** + + + + + +
IDCodeTypeDefaultDescriptionConstraints
141dnat_createbooleantrueНеобходимо создать DNAT-правило.
142internal_port_accessstring5432Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
diff --git a/generated/test/docs/vcexternalip_params_modify.md b/generated/test/docs/vcexternalip_params_modify.md new file mode 100644 index 0000000..f17b66c --- /dev/null +++ b/generated/test/docs/vcexternalip_params_modify.md @@ -0,0 +1,20 @@ +# Resource nubes_vcexternalip + +Service ID: `25` + +Service Name: Публичные IP адреса + +[Manual](vcexternalip.md) | [Create params](vcexternalip_params_create.md) | [Modify params](vcexternalip_params_modify.md) | [Output params](vcexternalip_outputs.md) | [Operations](vcexternalip_ops.md) | [Example](vcexternalip_example.md) + +## Modify params + + + + + + + + + + +
IDCodeTypeDescriptionConstraints
627service_uidstring
628ip_space_namestring
629dnat_createboolean
630snat_createboolean
631internal_port_accessstring
632internal_addr_accessstring
diff --git a/generated/test/docs/vmpostgre.md b/generated/test/docs/vmpostgre.md new file mode 100644 index 0000000..31d8dad --- /dev/null +++ b/generated/test/docs/vmpostgre.md @@ -0,0 +1,13 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## MAN + +
+Сервис настройки PostgreSQL для виртуальной машины.
Требует наличия ВМ +
diff --git a/generated/test/docs/vmpostgre_example.md b/generated/test/docs/vmpostgre_example.md new file mode 100644 index 0000000..12c950d --- /dev/null +++ b/generated/test/docs/vmpostgre_example.md @@ -0,0 +1,42 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Copy-ready manifest (`vmpostgre_main.tf`) + +```hcl +terraform { + required_providers { + nubes = { + source = "terra.k8c.ru/nubes/nubes" + version = "5.0.75" + } + } +} + +# Токен доступа api_token к Nubes API — замените на реальный. +provider "nubes" { + api_token = "token_***" + api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" +} +resource "nubes_vmpostgre" "baza" { +} +``` + +## Outputs usage + +```hcl +# Выходные параметры можно использовать как +nubes_vmpostgre.baza.state_params["имя_ключа"] +nubes_vmpostgre.baza.state_out["имя_ключа"] +nubes_vmpostgre.baza.state_params_flat["имя_ключа"] +nubes_vmpostgre.baza.state_out_flat["имя_ключа"] +nubes_vmpostgre.baza.vault_secrets["имя_ключа"] +nubes_vmpostgre.baza.vault_url +nubes_vmpostgre.baza.vault_user_path +nubes_vmpostgre.baza.vault_fields +``` diff --git a/generated/test/docs/vmpostgre_ops.md b/generated/test/docs/vmpostgre_ops.md new file mode 100644 index 0000000..a83096e --- /dev/null +++ b/generated/test/docs/vmpostgre_ops.md @@ -0,0 +1,10 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Operations + diff --git a/generated/test/docs/vmpostgre_outputs.md b/generated/test/docs/vmpostgre_outputs.md new file mode 100644 index 0000000..5c8d505 --- /dev/null +++ b/generated/test/docs/vmpostgre_outputs.md @@ -0,0 +1,18 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Output params + +- `state_params` (map) — параметры, отправленные в API при создании/изменении +- `state_out` (map) — ответ API с результатами/выходными значениями +- `state_params_flat` (map) — параметры в плоском виде (ключи с путями) +- `state_out_flat` (map) — ответы в плоском виде (ключи с путями) +- `vault_secrets` (map) — секреты, записанные в Vault для ресурса +- `vault_url` (string) — адрес Vault для ресурса +- `vault_user_path` (string) — путь пользователя в Vault +- `vault_fields` (list) — список ключей доступных секретов diff --git a/generated/test/docs/vmpostgre_params.md b/generated/test/docs/vmpostgre_params.md new file mode 100644 index 0000000..8b28da6 --- /dev/null +++ b/generated/test/docs/vmpostgre_params.md @@ -0,0 +1,12 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Parameters + +- [Create params](vmpostgre_params_create.md) +- [Modify params](vmpostgre_params_modify.md) diff --git a/generated/test/docs/vmpostgre_params_create.md b/generated/test/docs/vmpostgre_params_create.md new file mode 100644 index 0000000..f140764 --- /dev/null +++ b/generated/test/docs/vmpostgre_params_create.md @@ -0,0 +1,16 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Create params + +**Обязательные параметры (вводимые пользователем)** + +None. + +**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте** +None. diff --git a/generated/test/docs/vmpostgre_params_modify.md b/generated/test/docs/vmpostgre_params_modify.md new file mode 100644 index 0000000..9ce30ad --- /dev/null +++ b/generated/test/docs/vmpostgre_params_modify.md @@ -0,0 +1,11 @@ +# Resource nubes_vmpostgre + +Service ID: `32` + +Service Name: vc_vm_postgresql_std + +[Manual](vmpostgre.md) | [Create params](vmpostgre_params_create.md) | [Modify params](vmpostgre_params_modify.md) | [Output params](vmpostgre_outputs.md) | [Operations](vmpostgre_ops.md) | [Example](vmpostgre_example.md) + +## Modify params + +None. diff --git a/generated/test/go/.gitkeep b/generated/test/go/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/provider/internal/resources_gen/100_openwhisk_resource.go b/generated/test/go/100_openwhisk_resource.go similarity index 99% rename from provider/internal/resources_gen/100_openwhisk_resource.go rename to generated/test/go/100_openwhisk_resource.go index 0ee13f3..bdc6724 100644 --- a/provider/internal/resources_gen/100_openwhisk_resource.go +++ b/generated/test/go/100_openwhisk_resource.go @@ -244,8 +244,6 @@ func (r *OpenwhiskResource) Update(ctx context.Context, req resource.UpdateReque return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -253,6 +251,7 @@ func (r *OpenwhiskResource) Update(ctx context.Context, req resource.UpdateReque if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/110_dnszone_resource.go b/generated/test/go/110_dnszone_resource.go similarity index 99% rename from provider/internal/resources_gen/110_dnszone_resource.go rename to generated/test/go/110_dnszone_resource.go index 20201dd..0826696 100644 --- a/provider/internal/resources_gen/110_dnszone_resource.go +++ b/generated/test/go/110_dnszone_resource.go @@ -418,8 +418,6 @@ func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -427,6 +425,7 @@ func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/111_dnsrecord_resource.go b/generated/test/go/111_dnsrecord_resource.go similarity index 99% rename from provider/internal/resources_gen/111_dnsrecord_resource.go rename to generated/test/go/111_dnsrecord_resource.go index 03e9fa6..bb64255 100644 --- a/provider/internal/resources_gen/111_dnsrecord_resource.go +++ b/generated/test/go/111_dnsrecord_resource.go @@ -443,10 +443,6 @@ func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateReque return } - params := map[int]string{ - 256: resources_core.FormatString(plan.RecordInput), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -454,6 +450,9 @@ func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateReque if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 256: resources_core.FormatString(plan.RecordInput), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/112_tenant_resource.go b/generated/test/go/112_tenant_resource.go similarity index 99% rename from provider/internal/resources_gen/112_tenant_resource.go rename to generated/test/go/112_tenant_resource.go index 50b51a9..6838a3b 100644 --- a/provider/internal/resources_gen/112_tenant_resource.go +++ b/generated/test/go/112_tenant_resource.go @@ -301,8 +301,6 @@ func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -310,6 +308,7 @@ func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/113_vc_complex_resource.go b/generated/test/go/113_vc_complex_resource.go similarity index 99% rename from provider/internal/resources_gen/113_vc_complex_resource.go rename to generated/test/go/113_vc_complex_resource.go index dd143a4..dfb13ff 100644 --- a/provider/internal/resources_gen/113_vc_complex_resource.go +++ b/generated/test/go/113_vc_complex_resource.go @@ -480,8 +480,6 @@ func (r *VcComplexResource) Update(ctx context.Context, req resource.UpdateReque return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -489,6 +487,7 @@ func (r *VcComplexResource) Update(ctx context.Context, req resource.UpdateReque if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/114_gitea_complex_resource.go b/generated/test/go/114_gitea_complex_resource.go similarity index 99% rename from provider/internal/resources_gen/114_gitea_complex_resource.go rename to generated/test/go/114_gitea_complex_resource.go index d9b7146..dd9824e 100644 --- a/provider/internal/resources_gen/114_gitea_complex_resource.go +++ b/generated/test/go/114_gitea_complex_resource.go @@ -244,8 +244,6 @@ func (r *GiteaComplexResource) Update(ctx context.Context, req resource.UpdateRe return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -253,6 +251,7 @@ func (r *GiteaComplexResource) Update(ctx context.Context, req resource.UpdateRe if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/115_mariadb_database_resource.go b/generated/test/go/115_mariadb_database_resource.go similarity index 100% rename from provider/internal/resources_gen/115_mariadb_database_resource.go rename to generated/test/go/115_mariadb_database_resource.go diff --git a/provider/internal/resources_gen/115_mariadb_resource.go b/generated/test/go/115_mariadb_resource.go similarity index 99% rename from provider/internal/resources_gen/115_mariadb_resource.go rename to generated/test/go/115_mariadb_resource.go index f082ffc..2ae9571 100644 --- a/provider/internal/resources_gen/115_mariadb_resource.go +++ b/generated/test/go/115_mariadb_resource.go @@ -405,14 +405,6 @@ func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest return } - params := map[int]string{ - 883: resources_core.FormatString(plan.ClusterConfiguration), - 884: resources_core.FormatString(plan.AccessConfiguration), - 885: resources_core.FormatString(plan.MariadbConfiguration), - 886: resources_core.FormatString(plan.BackupConfiguration), - 887: resources_core.FormatString(plan.AutoscaleConfiguration), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -420,6 +412,13 @@ func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 883: resources_core.FormatString(plan.ClusterConfiguration), + 884: resources_core.FormatString(plan.AccessConfiguration), + 885: resources_core.FormatString(plan.MariadbConfiguration), + 886: resources_core.FormatString(plan.BackupConfiguration), + 887: resources_core.FormatString(plan.AutoscaleConfiguration), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/115_mariadb_user_resource.go b/generated/test/go/115_mariadb_user_resource.go similarity index 100% rename from provider/internal/resources_gen/115_mariadb_user_resource.go rename to generated/test/go/115_mariadb_user_resource.go diff --git a/provider/internal/resources_gen/116_kafka_resource.go b/generated/test/go/116_kafka_resource.go similarity index 99% rename from provider/internal/resources_gen/116_kafka_resource.go rename to generated/test/go/116_kafka_resource.go index 57fc61b..5c1f42a 100644 --- a/provider/internal/resources_gen/116_kafka_resource.go +++ b/generated/test/go/116_kafka_resource.go @@ -409,6 +409,13 @@ func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, return } + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } params := map[int]string{ 501: resources_core.FormatInt64(plan.ResourceInstances), 502: resources_core.FormatInt64(plan.ResourceMemory), @@ -417,14 +424,6 @@ func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, 505: resources_core.FormatBool(plan.NeedExternalAddressMaster), 506: resources_core.FormatString(plan.IpSpaceNameMaster), } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/116_kafka_topic_resource.go b/generated/test/go/116_kafka_topic_resource.go similarity index 100% rename from provider/internal/resources_gen/116_kafka_topic_resource.go rename to generated/test/go/116_kafka_topic_resource.go diff --git a/provider/internal/resources_gen/116_kafka_user_resource.go b/generated/test/go/116_kafka_user_resource.go similarity index 100% rename from provider/internal/resources_gen/116_kafka_user_resource.go rename to generated/test/go/116_kafka_user_resource.go diff --git a/provider/internal/resources_gen/117_nifi_resource.go b/generated/test/go/117_nifi_resource.go similarity index 99% rename from provider/internal/resources_gen/117_nifi_resource.go rename to generated/test/go/117_nifi_resource.go index 3303810..afe981b 100644 --- a/provider/internal/resources_gen/117_nifi_resource.go +++ b/generated/test/go/117_nifi_resource.go @@ -430,11 +430,6 @@ func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, r return } - params := map[int]string{ - 477: resources_core.FormatInt64(plan.Partitions), - 478: resources_core.FormatInt64(plan.Replicas), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -442,6 +437,10 @@ func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, r if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 477: resources_core.FormatInt64(plan.Partitions), + 478: resources_core.FormatInt64(plan.Replicas), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/119_akhq_resource.go b/generated/test/go/119_akhq_resource.go similarity index 99% rename from provider/internal/resources_gen/119_akhq_resource.go rename to generated/test/go/119_akhq_resource.go index cd1b8a6..2aadab6 100644 --- a/provider/internal/resources_gen/119_akhq_resource.go +++ b/generated/test/go/119_akhq_resource.go @@ -447,10 +447,6 @@ func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, r return } - params := map[int]string{ - 565: resources_core.FormatString(plan.KafkaUid), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -458,6 +454,9 @@ func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, r if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 565: resources_core.FormatString(plan.KafkaUid), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/120_clickhouse_database_resource.go b/generated/test/go/120_clickhouse_database_resource.go similarity index 100% rename from provider/internal/resources_gen/120_clickhouse_database_resource.go rename to generated/test/go/120_clickhouse_database_resource.go diff --git a/provider/internal/resources_gen/120_clickhouse_resource.go b/generated/test/go/120_clickhouse_resource.go similarity index 99% rename from provider/internal/resources_gen/120_clickhouse_resource.go rename to generated/test/go/120_clickhouse_resource.go index 426fa4e..ce9c514 100644 --- a/provider/internal/resources_gen/120_clickhouse_resource.go +++ b/generated/test/go/120_clickhouse_resource.go @@ -446,6 +446,13 @@ func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequ return } + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } params := map[int]string{ 844: resources_core.FormatString(plan.ClusterConfiguration), 845: resources_core.FormatString(plan.ClickhouseKeeperConfiguration), @@ -455,14 +462,6 @@ func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequ 849: resources_core.FormatString(plan.AutoscaleConfiguration), 850: resources_core.FormatString(plan.ClickhouseFilesConfiguration), } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/120_clickhouse_user_resource.go b/generated/test/go/120_clickhouse_user_resource.go similarity index 100% rename from provider/internal/resources_gen/120_clickhouse_user_resource.go rename to generated/test/go/120_clickhouse_user_resource.go diff --git a/provider/internal/resources_gen/12_s3_resource.go b/generated/test/go/12_s3_resource.go similarity index 99% rename from provider/internal/resources_gen/12_s3_resource.go rename to generated/test/go/12_s3_resource.go index c421eec..af49186 100644 --- a/provider/internal/resources_gen/12_s3_resource.go +++ b/generated/test/go/12_s3_resource.go @@ -340,12 +340,6 @@ func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, res return } - params := map[int]string{ - 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), - 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), - 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -353,6 +347,11 @@ func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, res if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), + 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), + 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/12_s3_sub_user_resource.go b/generated/test/go/12_s3_sub_user_resource.go similarity index 100% rename from provider/internal/resources_gen/12_s3_sub_user_resource.go rename to generated/test/go/12_s3_sub_user_resource.go diff --git a/provider/internal/resources_gen/13_s3bucket_resource.go b/generated/test/go/13_s3bucket_resource.go similarity index 99% rename from provider/internal/resources_gen/13_s3bucket_resource.go rename to generated/test/go/13_s3bucket_resource.go index 2dd54f8..df9acbe 100644 --- a/provider/internal/resources_gen/13_s3bucket_resource.go +++ b/generated/test/go/13_s3bucket_resource.go @@ -389,8 +389,6 @@ func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateReques return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -398,6 +396,7 @@ func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateReques if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/149_valo_tenant_resource.go b/generated/test/go/149_valo_tenant_resource.go similarity index 99% rename from provider/internal/resources_gen/149_valo_tenant_resource.go rename to generated/test/go/149_valo_tenant_resource.go index 5f05928..e85fe67 100644 --- a/provider/internal/resources_gen/149_valo_tenant_resource.go +++ b/generated/test/go/149_valo_tenant_resource.go @@ -339,11 +339,6 @@ func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequ return } - params := map[int]string{ - 659: resources_core.FormatString(plan.ValoOptions), - 660: resources_core.FormatInt64(plan.S3Storage), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -351,6 +346,10 @@ func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequ if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 659: resources_core.FormatString(plan.ValoOptions), + 660: resources_core.FormatInt64(plan.S3Storage), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go b/generated/test/go/150_k8s_sthutrval_cluster_resource.go similarity index 99% rename from provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go rename to generated/test/go/150_k8s_sthutrval_cluster_resource.go index 07df93a..b1ab187 100644 --- a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go +++ b/generated/test/go/150_k8s_sthutrval_cluster_resource.go @@ -369,13 +369,6 @@ func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.U return } - params := map[int]string{ - 756: resources_core.FormatString(plan.ClusterConfiguration), - 757: resources_core.FormatString(plan.ControlPlaneConfiguration), - 758: resources_core.FormatString(plan.WorkerConfiguration), - 759: resources_core.FormatString(plan.AccessConfiguration), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -383,6 +376,12 @@ func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.U if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 756: resources_core.FormatString(plan.ClusterConfiguration), + 757: resources_core.FormatString(plan.ControlPlaneConfiguration), + 758: resources_core.FormatString(plan.WorkerConfiguration), + 759: resources_core.FormatString(plan.AccessConfiguration), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go b/generated/test/go/150_k8s_sthutrval_cluster_user_resource.go similarity index 100% rename from provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go rename to generated/test/go/150_k8s_sthutrval_cluster_user_resource.go diff --git a/provider/internal/resources_gen/19_vc_org_resource.go b/generated/test/go/19_vc_org_resource.go similarity index 99% rename from provider/internal/resources_gen/19_vc_org_resource.go rename to generated/test/go/19_vc_org_resource.go index 15f3d2b..b9a8ecb 100644 --- a/provider/internal/resources_gen/19_vc_org_resource.go +++ b/generated/test/go/19_vc_org_resource.go @@ -315,10 +315,6 @@ func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 662: resources_core.FormatString(plan.VIPConfigure), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -326,6 +322,9 @@ func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 662: resources_core.FormatString(plan.VIPConfigure), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/19_vc_org_user_resource.go b/generated/test/go/19_vc_org_user_resource.go similarity index 100% rename from provider/internal/resources_gen/19_vc_org_user_resource.go rename to generated/test/go/19_vc_org_user_resource.go diff --git a/generated/test/go/1_dummy_resource.go b/generated/test/go/1_dummy_resource.go new file mode 100644 index 0000000..0abbb07 --- /dev/null +++ b/generated/test/go/1_dummy_resource.go @@ -0,0 +1,576 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: dummy +// Service ID: 1 + +var _ resource.Resource = &DummyResource{} +var _ resource.ResourceWithModifyPlan = &DummyResource{} +var _ resource.ResourceWithImportState = &DummyResource{} + +type DummyResource struct { + client *core.UniversalClient +} + +type DummyModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + // --- Redeploy support (ARCHITECTURE.md) --- + // git_revision: при изменении вызывает redeploy вместо modify. + // Опциональное поле — если не задано, modify работает как обычно. + GitRevision types.String `tfsdk:"git_revision"` + Arr types.String `tfsdk:"arr"` + ArrayMapFixedExample types.String `tfsdk:"array_map_fixed_example"` + Bodymessage types.String `tfsdk:"bodymessage"` + DurationMs types.Int64 `tfsdk:"duration_ms"` + FailAtStart types.Bool `tfsdk:"fail_at_start"` + FailInProgress types.Bool `tfsdk:"fail_in_progress"` + JsonExample types.String `tfsdk:"json_example"` + MapExample types.String `tfsdk:"map_example"` + MapFixed types.String `tfsdk:"map_fixed"` + NestedRefExample types.String `tfsdk:"nested_ref_example"` + ResourceRealm types.String `tfsdk:"resource_realm"` + WhereFail types.Int64 `tfsdk:"where_fail"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewDummyResource() resource.Resource { + return &DummyResource{} +} + +func (r *DummyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dummy" +} + +func (r *DummyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "arr": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "array_map_fixed_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Array of map-fixed Не изменяется после создания."}, + "bodymessage": schema.StringAttribute{Optional: true, MarkdownDescription: "Дата, уходящая в Vault"}, + "duration_ms": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "перед завершением подождать (sleep) столько миллисекунд"}, + "fail_at_start": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, завершить джоб, ничего не записывая в cmdb"}, + "fail_in_progress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb"}, + "json_example": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для тестирования интерфейса", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "map_example": schema.StringAttribute{Optional: true}, + "map_fixed": schema.StringAttribute{Optional: true, MarkdownDescription: "Новый тип поля Не изменяется после создания."}, + "nested_ref_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + "where_fail": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write"}, + // --- Redeploy support (ARCHITECTURE.md) --- + // При изменении git_revision вызывается redeploy вместо modify. + // Если не задано — modify работает без изменений. + "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *DummyModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *DummyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan DummyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Arr.IsNull() && !plan.Arr.IsUnknown() && !state.Arr.IsNull() && !state.Arr.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.Arr.ValueString(), state.Arr.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить arr", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ArrayMapFixedExample.IsNull() && !plan.ArrayMapFixedExample.IsUnknown() && !state.ArrayMapFixedExample.IsNull() && !state.ArrayMapFixedExample.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.ArrayMapFixedExample.ValueString(), state.ArrayMapFixedExample.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить array_map_fixed_example", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.MapFixed.IsNull() && !plan.MapFixed.IsUnknown() && !state.MapFixed.IsNull() && !state.MapFixed.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.MapFixed.ValueString(), state.MapFixed.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить map_fixed", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.NestedRefExample.IsNull() && !plan.NestedRefExample.IsUnknown() && !state.NestedRefExample.IsNull() && !state.NestedRefExample.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.NestedRefExample.ValueString(), state.NestedRefExample.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить nested_ref_example", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Arr.IsNull() { + resp.Diagnostics.AddError("Missing required attribute", "arr is required.") + return + } + if config.Arr.IsUnknown() { + return + } + if config.ResourceRealm.IsNull() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ResourceRealm.IsUnknown() { + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 198: resources_core.FormatInt64(config.DurationMs), + 199: resources_core.FormatBool(config.FailAtStart), + 200: resources_core.FormatBool(config.FailInProgress), + 201: resources_core.FormatInt64(config.WhereFail), + 242: resources_core.FormatString(config.ResourceRealm), + 286: resources_core.FormatString(config.Bodymessage), + 321: resources_core.FormatString(config.MapExample), + 322: resources_core.FormatString(config.JsonExample), + 396: resources_core.FormatString(config.NestedRefExample), + 647: resources_core.FormatString(config.MapFixed), + 654: resources_core.FormatString(config.ArrayMapFixedExample), + 863: resources_core.FormatString(config.Arr), + } + desiredDomain := "" + domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 1, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data DummyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Arr.IsNull() || data.Arr.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "arr is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 198: resources_core.FormatInt64(data.DurationMs), + 199: resources_core.FormatBool(data.FailAtStart), + 200: resources_core.FormatBool(data.FailInProgress), + 201: resources_core.FormatInt64(data.WhereFail), + 242: resources_core.FormatString(data.ResourceRealm), + 286: resources_core.FormatString(data.Bodymessage), + 321: resources_core.FormatString(data.MapExample), + 322: resources_core.FormatString(data.JsonExample), + 396: resources_core.FormatString(data.NestedRefExample), + 647: resources_core.FormatString(data.MapFixed), + 654: resources_core.FormatString(data.ArrayMapFixedExample), + 863: resources_core.FormatString(data.Arr), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 1, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "arr", Field: "Arr", Type: "string"}, + {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, + {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, + {Code: "durationMs", Field: "DurationMs", Type: "int64"}, + {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, + {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, + {Code: "jsonExample", Field: "JsonExample", Type: "string"}, + {Code: "mapExample", Field: "MapExample", Type: "string"}, + {Code: "mapFixed", Field: "MapFixed", Type: "string"}, + {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DummyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 1, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "arr", Field: "Arr", Type: "string"}, + {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, + {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, + {Code: "durationMs", Field: "DurationMs", Type: "int64"}, + {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, + {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, + {Code: "jsonExample", Field: "JsonExample", Type: "string"}, + {Code: "mapExample", Field: "MapExample", Type: "string"}, + {Code: "mapFixed", Field: "MapFixed", Type: "string"}, + {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan DummyModel + var state DummyModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.DurationMs.IsNull() != state.DurationMs.IsNull() || plan.DurationMs.IsUnknown() != state.DurationMs.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.DurationMs.IsNull() && !plan.DurationMs.IsUnknown() && !state.DurationMs.IsNull() && !state.DurationMs.IsUnknown() { + if plan.DurationMs.ValueInt64() != state.DurationMs.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.FailAtStart.IsNull() != state.FailAtStart.IsNull() || plan.FailAtStart.IsUnknown() != state.FailAtStart.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.FailAtStart.IsNull() && !plan.FailAtStart.IsUnknown() && !state.FailAtStart.IsNull() && !state.FailAtStart.IsUnknown() { + if plan.FailAtStart.ValueBool() != state.FailAtStart.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.FailInProgress.IsNull() != state.FailInProgress.IsNull() || plan.FailInProgress.IsUnknown() != state.FailInProgress.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.FailInProgress.IsNull() && !plan.FailInProgress.IsUnknown() && !state.FailInProgress.IsNull() && !state.FailInProgress.IsUnknown() { + if plan.FailInProgress.ValueBool() != state.FailInProgress.ValueBool() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.WhereFail.IsNull() != state.WhereFail.IsNull() || plan.WhereFail.IsUnknown() != state.WhereFail.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.WhereFail.IsNull() && !plan.WhereFail.IsUnknown() && !state.WhereFail.IsNull() && !state.WhereFail.IsUnknown() { + if plan.WhereFail.ValueInt64() != state.WhereFail.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.Bodymessage.IsNull() != state.Bodymessage.IsNull() || plan.Bodymessage.IsUnknown() != state.Bodymessage.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.Bodymessage.IsNull() && !plan.Bodymessage.IsUnknown() && !state.Bodymessage.IsNull() && !state.Bodymessage.IsUnknown() { + if !strings.EqualFold(plan.Bodymessage.ValueString(), state.Bodymessage.ValueString()) { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.MapExample.IsNull() != state.MapExample.IsNull() || plan.MapExample.IsUnknown() != state.MapExample.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.MapExample.IsNull() && !plan.MapExample.IsUnknown() && !state.MapExample.IsNull() && !state.MapExample.IsUnknown() { + if !strings.EqualFold(plan.MapExample.ValueString(), state.MapExample.ValueString()) { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.JsonExample.IsNull() != state.JsonExample.IsNull() || plan.JsonExample.IsUnknown() != state.JsonExample.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.JsonExample.IsNull() && !plan.JsonExample.IsUnknown() && !state.JsonExample.IsNull() && !state.JsonExample.IsUnknown() { + if !strings.EqualFold(plan.JsonExample.ValueString(), state.JsonExample.ValueString()) { + hasServiceParamChanges = true + } + } + } + // --- Redeploy support (ARCHITECTURE.md) --- + // Проверяем изменился ли git_revision. + // Если да — вызываем redeploy вместо modify. + // git_revision = "" (не задано) → обычный modify. + redeployRequested := false + if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { + if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { + redeployRequested = true + } + } + + if !hasServiceParamChanges && !redeployRequested { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + // --- Redeploy support (ARCHITECTURE.md) --- + if redeployRequested { + // Вызываем redeploy — пустой params (операция не требует CFS-параметров) + if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } else { + params := map[int]string{ + 287: resources_core.FormatInt64(plan.DurationMs), + 288: resources_core.FormatBool(plan.FailAtStart), + 289: resources_core.FormatBool(plan.FailInProgress), + 290: resources_core.FormatInt64(plan.WhereFail), + 291: resources_core.FormatString(plan.Bodymessage), + 373: resources_core.FormatString(plan.MapExample), + 374: resources_core.FormatString(plan.JsonExample), + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 1, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "arr", Field: "Arr", Type: "string"}, + {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "string"}, + {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, + {Code: "durationMs", Field: "DurationMs", Type: "int64"}, + {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, + {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, + {Code: "jsonExample", Field: "JsonExample", Type: "string"}, + {Code: "mapExample", Field: "MapExample", Type: "string"}, + {Code: "mapFixed", Field: "MapFixed", Type: "string"}, + {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + {Code: "whereFail", Field: "WhereFail", Type: "int64"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *DummyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state DummyModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *DummyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *DummyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/20_vc_org_saas_resource.go b/generated/test/go/20_vc_org_saas_resource.go similarity index 99% rename from provider/internal/resources_gen/20_vc_org_saas_resource.go rename to generated/test/go/20_vc_org_saas_resource.go index 319d7a4..6116681 100644 --- a/provider/internal/resources_gen/20_vc_org_saas_resource.go +++ b/generated/test/go/20_vc_org_saas_resource.go @@ -275,8 +275,6 @@ func (r *VcOrgSaasResource) Update(ctx context.Context, req resource.UpdateReque return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -284,6 +282,7 @@ func (r *VcOrgSaasResource) Update(ctx context.Context, req resource.UpdateReque if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/21_vc_vdc_resource.go b/generated/test/go/21_vc_vdc_resource.go similarity index 99% rename from provider/internal/resources_gen/21_vc_vdc_resource.go rename to generated/test/go/21_vc_vdc_resource.go index eb54f54..0cc7279 100644 --- a/provider/internal/resources_gen/21_vc_vdc_resource.go +++ b/generated/test/go/21_vc_vdc_resource.go @@ -463,12 +463,6 @@ func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 560: resources_core.FormatInt64(plan.CpuAllocated), - 561: resources_core.FormatInt64(plan.MemAllocated), - 562: resources_core.FormatString(plan.StorageConfig), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -476,6 +470,11 @@ func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 560: resources_core.FormatInt64(plan.CpuAllocated), + 561: resources_core.FormatInt64(plan.MemAllocated), + 562: resources_core.FormatString(plan.StorageConfig), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/22_vc_nsxt_resource.go b/generated/test/go/22_vc_nsxt_resource.go similarity index 99% rename from provider/internal/resources_gen/22_vc_nsxt_resource.go rename to generated/test/go/22_vc_nsxt_resource.go index 3f9e654..7184b1a 100644 --- a/provider/internal/resources_gen/22_vc_nsxt_resource.go +++ b/generated/test/go/22_vc_nsxt_resource.go @@ -493,13 +493,6 @@ func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 368: resources_core.FormatBool(plan.NeedEnableAVI), - 369: resources_core.FormatInt64(plan.VirtualServicesCount), - 372: resources_core.FormatString(plan.IpSpaceName), - 856: resources_core.FormatString(plan.QosProfile), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -507,6 +500,12 @@ func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 368: resources_core.FormatBool(plan.NeedEnableAVI), + 369: resources_core.FormatInt64(plan.VirtualServicesCount), + 372: resources_core.FormatString(plan.IpSpaceName), + 856: resources_core.FormatString(plan.QosProfile), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/23_vc_vm_resource.go b/generated/test/go/23_vc_vm_resource.go similarity index 99% rename from provider/internal/resources_gen/23_vc_vm_resource.go rename to generated/test/go/23_vc_vm_resource.go index b405e85..d5275df 100644 --- a/provider/internal/resources_gen/23_vc_vm_resource.go +++ b/generated/test/go/23_vc_vm_resource.go @@ -469,13 +469,6 @@ func (r *VcVmResource) Update(ctx context.Context, req resource.UpdateRequest, r return } - params := map[int]string{ - 186: resources_core.FormatString(plan.VmCpu), - 187: resources_core.FormatString(plan.VmRam), - 188: resources_core.FormatString(plan.VmDisksize), - 189: resources_core.FormatString(plan.AccessIpList), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -483,6 +476,12 @@ func (r *VcVmResource) Update(ctx context.Context, req resource.UpdateRequest, r if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 186: resources_core.FormatString(plan.VmCpu), + 187: resources_core.FormatString(plan.VmRam), + 188: resources_core.FormatString(plan.VmDisksize), + 189: resources_core.FormatString(plan.AccessIpList), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/25_vcexternalip_resource.go b/generated/test/go/25_vcexternalip_resource.go similarity index 99% rename from provider/internal/resources_gen/25_vcexternalip_resource.go rename to generated/test/go/25_vcexternalip_resource.go index c08274c..fc31693 100644 --- a/provider/internal/resources_gen/25_vcexternalip_resource.go +++ b/generated/test/go/25_vcexternalip_resource.go @@ -515,6 +515,13 @@ func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRe return } + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } params := map[int]string{ 627: resources_core.FormatString(plan.ServiceUid), 628: resources_core.FormatString(plan.IpSpaceName), @@ -523,14 +530,6 @@ func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRe 631: resources_core.FormatString(plan.InternalPortAccess), 632: resources_core.FormatString(plan.InternalAddrAccess), } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/26_vapp_resource.go b/generated/test/go/26_vapp_resource.go similarity index 99% rename from provider/internal/resources_gen/26_vapp_resource.go rename to generated/test/go/26_vapp_resource.go index 733d00c..c67b9f1 100644 --- a/provider/internal/resources_gen/26_vapp_resource.go +++ b/generated/test/go/26_vapp_resource.go @@ -466,8 +466,6 @@ func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, r return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -475,6 +473,7 @@ func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, r if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/27_vc_vm_v2_resource.go b/generated/test/go/27_vc_vm_v2_resource.go similarity index 99% rename from provider/internal/resources_gen/27_vc_vm_v2_resource.go rename to generated/test/go/27_vc_vm_v2_resource.go index f8bd0a2..de78745 100644 --- a/provider/internal/resources_gen/27_vc_vm_v2_resource.go +++ b/generated/test/go/27_vc_vm_v2_resource.go @@ -265,10 +265,6 @@ func (r *VcVmV2Resource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 302: resources_core.FormatString(plan.ServerListJson), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -276,6 +272,9 @@ func (r *VcVmV2Resource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 302: resources_core.FormatString(plan.ServerListJson), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/28_vc_vm_v3_resource.go b/generated/test/go/28_vc_vm_v3_resource.go similarity index 83% rename from provider/internal/resources_gen/28_vc_vm_v3_resource.go rename to generated/test/go/28_vc_vm_v3_resource.go index 7efe110..9145c69 100644 --- a/provider/internal/resources_gen/28_vc_vm_v3_resource.go +++ b/generated/test/go/28_vc_vm_v3_resource.go @@ -31,10 +31,14 @@ type VcVmV3Resource struct { } type VcVmV3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + // --- Redeploy support (ARCHITECTURE.md) --- + // git_revision: при изменении вызывает redeploy вместо modify. + // Опциональное поле — если не задано, modify работает как обычно. + GitRevision types.String `tfsdk:"git_revision"` AccessIpList types.String `tfsdk:"access_ip_list"` AccessPortList types.String `tfsdk:"access_port_list"` CloudInit types.String `tfsdk:"cloud_init"` @@ -69,22 +73,26 @@ func (r *VcVmV3Resource) Metadata(ctx context.Context, req resource.MetadataRequ func (r *VcVmV3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, - "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, - "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Required: true}, - "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, - "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, - "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, - "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, - "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, + "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, + "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, + "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, + "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "ip_space_name": schema.StringAttribute{Required: true}, + "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, + "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, + "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, + "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, + "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, + "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, + "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, + // --- Redeploy support (ARCHITECTURE.md) --- + // При изменении git_revision вызывается redeploy вместо modify. + // Если не задано — modify работает без изменений. + "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, @@ -537,8 +545,18 @@ func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, } } } + // --- Redeploy support (ARCHITECTURE.md) --- + // Проверяем изменился ли git_revision. + // Если да — вызываем redeploy вместо modify. + // git_revision = "" (не задано) → обычный modify. + redeployRequested := false + if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { + if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { + redeployRequested = true + } + } - if !hasServiceParamChanges { + if !hasServiceParamChanges && !redeployRequested { plan.ID = instanceID plan.StateParams = state.StateParams plan.StateOut = state.StateOut @@ -552,15 +570,6 @@ func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 493: resources_core.FormatInt64(plan.VmCpu), - 494: resources_core.FormatInt64(plan.VmRam), - 495: resources_core.FormatInt64(plan.VmDisk), - 496: resources_core.FormatString(plan.IpSpaceName), - 497: resources_core.FormatString(plan.AccessIpList), - 498: resources_core.FormatString(plan.AccessPortList), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -568,9 +577,26 @@ func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return + // --- Redeploy support (ARCHITECTURE.md) --- + if redeployRequested { + // Вызываем redeploy — пустой params (операция не требует CFS-параметров) + if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } else { + params := map[int]string{ + 493: resources_core.FormatInt64(plan.VmCpu), + 494: resources_core.FormatInt64(plan.VmRam), + 495: resources_core.FormatInt64(plan.VmDisk), + 496: resources_core.FormatString(plan.IpSpaceName), + 497: resources_core.FormatString(plan.AccessIpList), + 498: resources_core.FormatString(plan.AccessPortList), + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } } plan.ID = instanceID diff --git a/provider/internal/resources_gen/29_vc_vdc_group_resource.go b/generated/test/go/29_vc_vdc_group_resource.go similarity index 99% rename from provider/internal/resources_gen/29_vc_vdc_group_resource.go rename to generated/test/go/29_vc_vdc_group_resource.go index f737ec1..aebdf49 100644 --- a/provider/internal/resources_gen/29_vc_vdc_group_resource.go +++ b/generated/test/go/29_vc_vdc_group_resource.go @@ -352,8 +352,6 @@ func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequ return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -361,6 +359,7 @@ func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequ if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go b/generated/test/go/29_vc_vdc_group_vdc_resource.go similarity index 100% rename from provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go rename to generated/test/go/29_vc_vdc_group_vdc_resource.go diff --git a/provider/internal/resources_gen/2_template_resource.go b/generated/test/go/2_template_resource.go similarity index 99% rename from provider/internal/resources_gen/2_template_resource.go rename to generated/test/go/2_template_resource.go index 2c46a54..f360e1a 100644 --- a/provider/internal/resources_gen/2_template_resource.go +++ b/generated/test/go/2_template_resource.go @@ -327,11 +327,6 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques return } - params := map[int]string{ - 816: resources_core.FormatString(plan.ClusterConfiguration), - 817: resources_core.FormatString(plan.AccessConfiguration), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -339,6 +334,10 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 816: resources_core.FormatString(plan.ClusterConfiguration), + 817: resources_core.FormatString(plan.AccessConfiguration), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/32_vmpostgre_resource.go b/generated/test/go/32_vmpostgre_resource.go similarity index 99% rename from provider/internal/resources_gen/32_vmpostgre_resource.go rename to generated/test/go/32_vmpostgre_resource.go index db3ca98..2c99ecc 100644 --- a/provider/internal/resources_gen/32_vmpostgre_resource.go +++ b/generated/test/go/32_vmpostgre_resource.go @@ -244,8 +244,6 @@ func (r *VmpostgreResource) Update(ctx context.Context, req resource.UpdateReque return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -253,6 +251,7 @@ func (r *VmpostgreResource) Update(ctx context.Context, req resource.UpdateReque if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/50_nextcloud_backup_resource.go b/generated/test/go/50_nextcloud_backup_resource.go similarity index 100% rename from provider/internal/resources_gen/50_nextcloud_backup_resource.go rename to generated/test/go/50_nextcloud_backup_resource.go diff --git a/provider/internal/resources_gen/50_nextcloud_resource.go b/generated/test/go/50_nextcloud_resource.go similarity index 99% rename from provider/internal/resources_gen/50_nextcloud_resource.go rename to generated/test/go/50_nextcloud_resource.go index 6183117..6068c97 100644 --- a/provider/internal/resources_gen/50_nextcloud_resource.go +++ b/generated/test/go/50_nextcloud_resource.go @@ -353,11 +353,6 @@ func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateReque return } - params := map[int]string{ - 1059: resources_core.FormatString(plan.ClusterConfiguration), - 1060: resources_core.FormatString(plan.AppConfiguration), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -365,6 +360,10 @@ func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateReque if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 1059: resources_core.FormatString(plan.ClusterConfiguration), + 1060: resources_core.FormatString(plan.AppConfiguration), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/81_superset_resource.go b/generated/test/go/81_superset_resource.go similarity index 99% rename from provider/internal/resources_gen/81_superset_resource.go rename to generated/test/go/81_superset_resource.go index 8a8010c..424ce42 100644 --- a/provider/internal/resources_gen/81_superset_resource.go +++ b/generated/test/go/81_superset_resource.go @@ -361,8 +361,6 @@ func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateReques return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -370,6 +368,7 @@ func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateReques if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/82_harbor_resource.go b/generated/test/go/82_harbor_resource.go similarity index 99% rename from provider/internal/resources_gen/82_harbor_resource.go rename to generated/test/go/82_harbor_resource.go index d29c149..453bfaa 100644 --- a/provider/internal/resources_gen/82_harbor_resource.go +++ b/generated/test/go/82_harbor_resource.go @@ -390,12 +390,6 @@ func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 822: resources_core.FormatInt64(plan.ResourceInstances), - 823: resources_core.FormatInt64(plan.ResourceCPU), - 824: resources_core.FormatInt64(plan.ResourceMemory), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -403,6 +397,11 @@ func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 822: resources_core.FormatInt64(plan.ResourceInstances), + 823: resources_core.FormatInt64(plan.ResourceCPU), + 824: resources_core.FormatInt64(plan.ResourceMemory), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/88_k8s_ziti_controller_resource.go b/generated/test/go/88_k8s_ziti_controller_resource.go similarity index 99% rename from provider/internal/resources_gen/88_k8s_ziti_controller_resource.go rename to generated/test/go/88_k8s_ziti_controller_resource.go index a5d7a93..02b01e3 100644 --- a/provider/internal/resources_gen/88_k8s_ziti_controller_resource.go +++ b/generated/test/go/88_k8s_ziti_controller_resource.go @@ -346,8 +346,6 @@ func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.Upd return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -355,6 +353,7 @@ func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.Upd if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/89_flask_resource.go b/generated/test/go/89_flask_resource.go similarity index 84% rename from provider/internal/resources_gen/89_flask_resource.go rename to generated/test/go/89_flask_resource.go index d0ee99f..40a4919 100644 --- a/provider/internal/resources_gen/89_flask_resource.go +++ b/generated/test/go/89_flask_resource.go @@ -30,10 +30,14 @@ type FlaskResource struct { } type FlaskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + // --- Redeploy support (ARCHITECTURE.md) --- + // git_revision: при изменении вызывает redeploy вместо modify. + // Опциональное поле — если не задано, modify работает как обычно. + GitRevision types.String `tfsdk:"git_revision"` AccessConfiguration types.String `tfsdk:"access_configuration"` AppConfiguration types.String `tfsdk:"app_configuration"` ClusterConfiguration types.String `tfsdk:"cluster_configuration"` @@ -61,15 +65,19 @@ func (r *FlaskResource) Metadata(ctx context.Context, req resource.MetadataReque func (r *FlaskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "app_configuration": schema.StringAttribute{Required: true}, - "cluster_configuration": schema.StringAttribute{Required: true}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, - "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "app_configuration": schema.StringAttribute{Required: true}, + "cluster_configuration": schema.StringAttribute{Required: true}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, + "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + // --- Redeploy support (ARCHITECTURE.md) --- + // При изменении git_revision вызывается redeploy вместо modify. + // Если не задано — modify работает без изменений. + "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, @@ -354,8 +362,18 @@ func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, } } } + // --- Redeploy support (ARCHITECTURE.md) --- + // Проверяем изменился ли git_revision. + // Если да — вызываем redeploy вместо modify. + // git_revision = "" (не задано) → обычный modify. + redeployRequested := false + if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { + if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { + redeployRequested = true + } + } - if !hasServiceParamChanges { + if !hasServiceParamChanges && !redeployRequested { plan.ID = instanceID plan.StateParams = state.StateParams plan.StateOut = state.StateOut @@ -369,12 +387,6 @@ func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 1042: resources_core.FormatString(plan.ClusterConfiguration), - 1043: resources_core.FormatString(plan.AppConfiguration), - 1044: resources_core.FormatString(plan.JsonEnv), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -382,9 +394,23 @@ func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return + // --- Redeploy support (ARCHITECTURE.md) --- + if redeployRequested { + // Вызываем redeploy — пустой params (операция не требует CFS-параметров) + if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } else { + params := map[int]string{ + 1042: resources_core.FormatString(plan.ClusterConfiguration), + 1043: resources_core.FormatString(plan.AppConfiguration), + 1044: resources_core.FormatString(plan.JsonEnv), + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } } plan.ID = instanceID diff --git a/provider/internal/resources_gen/90_postgres_backup_resource.go b/generated/test/go/90_postgres_backup_resource.go similarity index 100% rename from provider/internal/resources_gen/90_postgres_backup_resource.go rename to generated/test/go/90_postgres_backup_resource.go diff --git a/provider/internal/resources_gen/90_postgres_database_resource.go b/generated/test/go/90_postgres_database_resource.go similarity index 100% rename from provider/internal/resources_gen/90_postgres_database_resource.go rename to generated/test/go/90_postgres_database_resource.go diff --git a/provider/internal/resources_gen/90_postgres_resource.go b/generated/test/go/90_postgres_resource.go similarity index 99% rename from provider/internal/resources_gen/90_postgres_resource.go rename to generated/test/go/90_postgres_resource.go index 94da64a..77912fd 100644 --- a/provider/internal/resources_gen/90_postgres_resource.go +++ b/generated/test/go/90_postgres_resource.go @@ -420,6 +420,13 @@ func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateReques return } + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } params := map[int]string{ 795: resources_core.FormatString(plan.ClusterConfiguration), 796: resources_core.FormatString(plan.AccessConfiguration), @@ -428,14 +435,6 @@ func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateReques 799: resources_core.FormatString(plan.BackupConfiguration), 800: resources_core.FormatString(plan.AutoscaleConfiguration), } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/90_postgres_user_resource.go b/generated/test/go/90_postgres_user_resource.go similarity index 100% rename from provider/internal/resources_gen/90_postgres_user_resource.go rename to generated/test/go/90_postgres_user_resource.go diff --git a/provider/internal/resources_gen/91_redis_resource.go b/generated/test/go/91_redis_resource.go similarity index 99% rename from provider/internal/resources_gen/91_redis_resource.go rename to generated/test/go/91_redis_resource.go index 5998295..0ad1e71 100644 --- a/provider/internal/resources_gen/91_redis_resource.go +++ b/generated/test/go/91_redis_resource.go @@ -327,11 +327,6 @@ func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 1064: resources_core.FormatString(plan.ClusterConfiguration), - 1065: resources_core.FormatString(plan.AccessConfiguration), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -339,6 +334,10 @@ func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{ + 1064: resources_core.FormatString(plan.ClusterConfiguration), + 1065: resources_core.FormatString(plan.AccessConfiguration), + } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/92_mongodb_resource.go b/generated/test/go/92_mongodb_resource.go similarity index 99% rename from provider/internal/resources_gen/92_mongodb_resource.go rename to generated/test/go/92_mongodb_resource.go index 8c3be63..7dd5671 100644 --- a/provider/internal/resources_gen/92_mongodb_resource.go +++ b/generated/test/go/92_mongodb_resource.go @@ -410,6 +410,13 @@ func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest return } + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } params := map[int]string{ 648: resources_core.FormatInt64(plan.ResourceInstances), 649: resources_core.FormatInt64(plan.ResourceMemory), @@ -418,14 +425,6 @@ func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest 653: resources_core.FormatString(plan.IpSpaceNameMaster), 890: resources_core.FormatString(plan.MasterAccessList), } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/92_mongodb_user_resource.go b/generated/test/go/92_mongodb_user_resource.go similarity index 100% rename from provider/internal/resources_gen/92_mongodb_user_resource.go rename to generated/test/go/92_mongodb_user_resource.go diff --git a/provider/internal/resources_gen/93_rabbitmq_resource.go b/generated/test/go/93_rabbitmq_resource.go similarity index 99% rename from provider/internal/resources_gen/93_rabbitmq_resource.go rename to generated/test/go/93_rabbitmq_resource.go index f8286e2..7c90da2 100644 --- a/provider/internal/resources_gen/93_rabbitmq_resource.go +++ b/generated/test/go/93_rabbitmq_resource.go @@ -366,6 +366,13 @@ func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateReques return } + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } params := map[int]string{ 304: resources_core.FormatInt64(plan.ResourceCPU), 305: resources_core.FormatInt64(plan.ResourceMemory), @@ -374,14 +381,6 @@ func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateReques 308: resources_core.FormatBool(plan.NeedExternalAddressMaster), 545: resources_core.FormatString(plan.IpSpaceNameMaster), } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/provider/internal/resources_gen/94_lucee_resource.go b/generated/test/go/94_lucee_resource.go similarity index 83% rename from provider/internal/resources_gen/94_lucee_resource.go rename to generated/test/go/94_lucee_resource.go index bcb7439..eaa6524 100644 --- a/provider/internal/resources_gen/94_lucee_resource.go +++ b/generated/test/go/94_lucee_resource.go @@ -30,10 +30,14 @@ type LuceeResource struct { } type LuceeModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + // --- Redeploy support (ARCHITECTURE.md) --- + // git_revision: при изменении вызывает redeploy вместо modify. + // Опциональное поле — если не задано, modify работает как обычно. + GitRevision types.String `tfsdk:"git_revision"` AccessConfiguration types.String `tfsdk:"access_configuration"` AppConfiguration types.String `tfsdk:"app_configuration"` ClusterConfiguration types.String `tfsdk:"cluster_configuration"` @@ -61,15 +65,19 @@ func (r *LuceeResource) Metadata(ctx context.Context, req resource.MetadataReque func (r *LuceeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "app_configuration": schema.StringAttribute{Required: true}, - "cluster_configuration": schema.StringAttribute{Required: true}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "app_configuration": schema.StringAttribute{Required: true}, + "cluster_configuration": schema.StringAttribute{Required: true}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, + "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + // --- Redeploy support (ARCHITECTURE.md) --- + // При изменении git_revision вызывается redeploy вместо modify. + // Если не задано — modify работает без изменений. + "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, @@ -354,8 +362,18 @@ func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, } } } + // --- Redeploy support (ARCHITECTURE.md) --- + // Проверяем изменился ли git_revision. + // Если да — вызываем redeploy вместо modify. + // git_revision = "" (не задано) → обычный modify. + redeployRequested := false + if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { + if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { + redeployRequested = true + } + } - if !hasServiceParamChanges { + if !hasServiceParamChanges && !redeployRequested { plan.ID = instanceID plan.StateParams = state.StateParams plan.StateOut = state.StateOut @@ -369,12 +387,6 @@ func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 1024: resources_core.FormatString(plan.ClusterConfiguration), - 1025: resources_core.FormatString(plan.AppConfiguration), - 1026: resources_core.FormatString(plan.JsonEnv), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -382,9 +394,23 @@ func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return + // --- Redeploy support (ARCHITECTURE.md) --- + if redeployRequested { + // Вызываем redeploy — пустой params (операция не требует CFS-параметров) + if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } else { + params := map[int]string{ + 1024: resources_core.FormatString(plan.ClusterConfiguration), + 1025: resources_core.FormatString(plan.AppConfiguration), + 1026: resources_core.FormatString(plan.JsonEnv), + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } } plan.ID = instanceID diff --git a/provider/internal/resources_gen/95_nodejs_resource.go b/generated/test/go/95_nodejs_resource.go similarity index 84% rename from provider/internal/resources_gen/95_nodejs_resource.go rename to generated/test/go/95_nodejs_resource.go index e638e34..9555693 100644 --- a/provider/internal/resources_gen/95_nodejs_resource.go +++ b/generated/test/go/95_nodejs_resource.go @@ -30,10 +30,14 @@ type NodejsResource struct { } type NodejsModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + // --- Redeploy support (ARCHITECTURE.md) --- + // git_revision: при изменении вызывает redeploy вместо modify. + // Опциональное поле — если не задано, modify работает как обычно. + GitRevision types.String `tfsdk:"git_revision"` AccessConfiguration types.String `tfsdk:"access_configuration"` AppConfiguration types.String `tfsdk:"app_configuration"` ClusterConfiguration types.String `tfsdk:"cluster_configuration"` @@ -61,15 +65,19 @@ func (r *NodejsResource) Metadata(ctx context.Context, req resource.MetadataRequ func (r *NodejsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "app_configuration": schema.StringAttribute{Required: true}, - "cluster_configuration": schema.StringAttribute{Required: true}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "access_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + "app_configuration": schema.StringAttribute{Required: true}, + "cluster_configuration": schema.StringAttribute{Required: true}, + "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, + "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, + // --- Redeploy support (ARCHITECTURE.md) --- + // При изменении git_revision вызывается redeploy вместо modify. + // Если не задано — modify работает без изменений. + "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, @@ -354,8 +362,18 @@ func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, } } } + // --- Redeploy support (ARCHITECTURE.md) --- + // Проверяем изменился ли git_revision. + // Если да — вызываем redeploy вместо modify. + // git_revision = "" (не задано) → обычный modify. + redeployRequested := false + if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { + if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { + redeployRequested = true + } + } - if !hasServiceParamChanges { + if !hasServiceParamChanges && !redeployRequested { plan.ID = instanceID plan.StateParams = state.StateParams plan.StateOut = state.StateOut @@ -369,12 +387,6 @@ func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, return } - params := map[int]string{ - 1033: resources_core.FormatString(plan.ClusterConfiguration), - 1034: resources_core.FormatString(plan.AppConfiguration), - 1035: resources_core.FormatString(plan.JsonEnv), - } - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -382,9 +394,23 @@ func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return + // --- Redeploy support (ARCHITECTURE.md) --- + if redeployRequested { + // Вызываем redeploy — пустой params (операция не требует CFS-параметров) + if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } else { + params := map[int]string{ + 1033: resources_core.FormatString(plan.ClusterConfiguration), + 1034: resources_core.FormatString(plan.AppConfiguration), + 1035: resources_core.FormatString(plan.JsonEnv), + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } } plan.ID = instanceID diff --git a/generated/test/go/96_pgadmin_resource.go b/generated/test/go/96_pgadmin_resource.go new file mode 100644 index 0000000..bc4b1b0 --- /dev/null +++ b/generated/test/go/96_pgadmin_resource.go @@ -0,0 +1,510 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: pgadmin +// Service ID: 96 + +var _ resource.Resource = &PgadminResource{} +var _ resource.ResourceWithModifyPlan = &PgadminResource{} +var _ resource.ResourceWithImportState = &PgadminResource{} + +type PgadminResource struct { + client *core.UniversalClient +} + +type PgadminModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + // --- Redeploy support (ARCHITECTURE.md) --- + // git_revision: при изменении вызывает redeploy вместо modify. + // Опциональное поле — если не задано, modify работает как обычно. + GitRevision types.String `tfsdk:"git_revision"` + Domain types.String `tfsdk:"domain"` + Login types.String `tfsdk:"login"` + Password types.String `tfsdk:"password"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceDisk types.Int64 `tfsdk:"resource_disk"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewPgadminResource() resource.Resource { + return &PgadminResource{} +} + +func (r *PgadminResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_pgadmin" +} + +func (r *PgadminResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "login": schema.StringAttribute{Required: true, MarkdownDescription: "Логин для подключения к pgAdmin, в формате email (login@email.net) Не изменяется после создания."}, + "password": schema.StringAttribute{Required: true, MarkdownDescription: "Пароль для подключения к pgAdmin Не изменяется после создания.", Sensitive: true}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, + "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + // --- Redeploy support (ARCHITECTURE.md) --- + // При изменении git_revision вызывается redeploy вместо modify. + // Если не задано — modify работает без изменений. + "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *PgadminModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *PgadminModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan PgadminModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Login.IsNull() && !plan.Login.IsUnknown() && !state.Login.IsNull() && !state.Login.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.Login.ValueString(), state.Login.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить login", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.Password.IsNull() && !plan.Password.IsUnknown() && !state.Password.IsNull() && !state.Password.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.Password.ValueString(), state.Password.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить password", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.Login.IsNull() { + resp.Diagnostics.AddError("Missing required attribute", "login is required.") + return + } + if config.Login.IsUnknown() { + return + } + if config.Password.IsNull() { + resp.Diagnostics.AddError("Missing required attribute", "password is required.") + return + } + if config.Password.IsUnknown() { + return + } + if config.ResourceRealm.IsNull() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ResourceRealm.IsUnknown() { + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 164: resources_core.FormatString(config.Domain), + 165: resources_core.FormatInt64(config.ResourceCPU), + 166: resources_core.FormatInt64(config.ResourceMemory), + 167: resources_core.FormatInt64(config.ResourceDisk), + 169: resources_core.FormatString(config.ResourceRealm), + 170: resources_core.FormatString(config.Login), + 171: resources_core.FormatString(config.Password), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 96, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *PgadminResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data PgadminModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.Login.IsNull() || data.Login.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "login is required.") + return + } + if data.Password.IsNull() || data.Password.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "password is required.") + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 164: resources_core.FormatString(data.Domain), + 165: resources_core.FormatInt64(data.ResourceCPU), + 166: resources_core.FormatInt64(data.ResourceMemory), + 167: resources_core.FormatInt64(data.ResourceDisk), + 169: resources_core.FormatString(data.ResourceRealm), + 170: resources_core.FormatString(data.Login), + 171: resources_core.FormatString(data.Password), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 96, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "login", Field: "Login", Type: "string"}, + {Code: "password", Field: "Password", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PgadminResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state PgadminModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 96, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "login", Field: "Login", Type: "string"}, + {Code: "password", Field: "Password", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *PgadminResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan PgadminModel + var state PgadminModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { + if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { + hasServiceParamChanges = true + } + } + } + // --- Redeploy support (ARCHITECTURE.md) --- + // Проверяем изменился ли git_revision. + // Если да — вызываем redeploy вместо modify. + // git_revision = "" (не задано) → обычный modify. + redeployRequested := false + if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { + if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { + redeployRequested = true + } + } + + if !hasServiceParamChanges && !redeployRequested { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + // --- Redeploy support (ARCHITECTURE.md) --- + if redeployRequested { + // Вызываем redeploy — пустой params (операция не требует CFS-параметров) + if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } else { + params := map[int]string{ + 172: resources_core.FormatInt64(plan.ResourceCPU), + 173: resources_core.FormatInt64(plan.ResourceMemory), + 174: resources_core.FormatInt64(plan.ResourceDisk), + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 96, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "login", Field: "Login", Type: "string"}, + {Code: "password", Field: "Password", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *PgadminResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state PgadminModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *PgadminResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *PgadminResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/97_nodered_resource.go b/generated/test/go/97_nodered_resource.go similarity index 99% rename from provider/internal/resources_gen/97_nodered_resource.go rename to generated/test/go/97_nodered_resource.go index af05cdd..2a1a8cb 100644 --- a/provider/internal/resources_gen/97_nodered_resource.go +++ b/generated/test/go/97_nodered_resource.go @@ -356,8 +356,6 @@ func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest return } - params := map[int]string{} - operationTimeout := "" if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() @@ -365,6 +363,7 @@ func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) } + params := map[int]string{} if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/generated/test/go/98_http_resource.go b/generated/test/go/98_http_resource.go new file mode 100644 index 0000000..f21348a --- /dev/null +++ b/generated/test/go/98_http_resource.go @@ -0,0 +1,504 @@ +package resources_gen + +import ( + "context" + "strings" + + "terraform-provider-nubes/internal/core" + "terraform-provider-nubes/internal/resources_core" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Code generated by tools/gen_v2. DO NOT EDIT. +// Service: http +// Service ID: 98 + +var _ resource.Resource = &HttpResource{} +var _ resource.ResourceWithModifyPlan = &HttpResource{} +var _ resource.ResourceWithImportState = &HttpResource{} + +type HttpResource struct { + client *core.UniversalClient +} + +type HttpModel struct { + ID types.String `tfsdk:"id"` + ResourceName types.String `tfsdk:"resource_name"` + OperationTimeout types.String `tfsdk:"operation_timeout"` + LogLevel types.String `tfsdk:"log_level"` + // --- Redeploy support (ARCHITECTURE.md) --- + // git_revision: при изменении вызывает redeploy вместо modify. + // Опциональное поле — если не задано, modify работает как обычно. + GitRevision types.String `tfsdk:"git_revision"` + Domain types.String `tfsdk:"domain"` + IngressAnnotations types.String `tfsdk:"ingress_annotations"` + JsonEnv types.String `tfsdk:"json_env"` + RegistryPath types.String `tfsdk:"registry_path"` + ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` + ResourceInstances types.Int64 `tfsdk:"resource_instances"` + ResourceMemory types.Int64 `tfsdk:"resource_memory"` + ResourceRealm types.String `tfsdk:"resource_realm"` + SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` + AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` + StateParams types.Map `tfsdk:"state_params"` + StateOut types.Map `tfsdk:"state_out"` + StateParamsFlat types.Map `tfsdk:"state_params_flat"` + StateOutFlat types.Map `tfsdk:"state_out_flat"` + VaultSecrets types.Map `tfsdk:"vault_secrets"` + VaultUrl types.String `tfsdk:"vault_url"` + VaultUserPath types.String `tfsdk:"vault_user_path"` + VaultFields types.List `tfsdk:"vault_fields"` +} + +func NewHttpResource() resource.Resource { + return &HttpResource{} +} + +func (r *HttpResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_http" +} + +func (r *HttpResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + attrs := map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, + "resource_name": schema.StringAttribute{Required: true}, + "operation_timeout": schema.StringAttribute{Optional: true}, + "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, + "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, + "ingress_annotations": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Формат ключ/значение
Поддерживает пока простые аннотации Не изменяется после создания."}, + "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания."}, + "registry_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nexus-sa.tst.nubes.ru/docker-nubes/jolt"), MarkdownDescription: "Путь до image, который будет установлен как deployment Не изменяется после создания."}, + "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili)"}, + "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, + "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, + "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, + // --- Redeploy support (ARCHITECTURE.md) --- + // При изменении git_revision вызывается redeploy вместо modify. + // Если не задано — modify работает без изменений. + "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, + "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, + "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, + "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, + "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, + "vault_url": schema.StringAttribute{Computed: true}, + "vault_user_path": schema.StringAttribute{Computed: true}, + "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, + } + + resp.Schema = schema.Schema{Attributes: attrs} +} + +func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.client == nil { + return + } + + var config *HttpModel + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + if config == nil { + return + } + + var state *HttpModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { + return + } + + if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { + var plan HttpModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { + if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { + resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") + return + } + } + if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.IngressAnnotations.IsNull() && !plan.IngressAnnotations.IsUnknown() && !state.IngressAnnotations.IsNull() && !state.IngressAnnotations.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.IngressAnnotations.ValueString(), state.IngressAnnotations.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить ingress_annotations", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить json_env", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.RegistryPath.IsNull() && !plan.RegistryPath.IsUnknown() && !state.RegistryPath.IsNull() && !state.RegistryPath.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.RegistryPath.ValueString(), state.RegistryPath.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить registry_path", "Параметр задается при создании и не может быть изменен.") + return + } + } + if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { + // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID + // в lowercase, пользователь мог написать upper/mixed. Это одно и то же + // значение, менять его нельзя только если оно реально другое. + if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { + resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") + return + } + } + return + } + if config.ResourceRealm.IsNull() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + if config.ResourceRealm.IsUnknown() { + return + } + + if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { + return + } + adoptExistingOnCreate := false + if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { + adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() + } + params := map[int]string{ + 212: resources_core.FormatString(config.Domain), + 213: resources_core.FormatInt64(config.ResourceCPU), + 214: resources_core.FormatInt64(config.ResourceMemory), + 215: resources_core.FormatInt64(config.ResourceInstances), + 216: resources_core.FormatString(config.ResourceRealm), + 217: resources_core.FormatString(config.RegistryPath), + 241: resources_core.FormatString(config.JsonEnv), + 782: resources_core.FormatString(config.IngressAnnotations), + } + desiredDomain := "" + if !config.Domain.IsNull() && !config.Domain.IsUnknown() { + desiredDomain = config.Domain.ValueString() + } + domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + + resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 98, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) +} + +func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data HttpModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { + resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") + return + } + + resourceName := data.ResourceName.ValueString() + desiredDomain := "" + if !data.Domain.IsNull() && !data.Domain.IsUnknown() { + desiredDomain = data.Domain.ValueString() + } + domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} + resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) + + params := map[int]string{ + 212: resources_core.FormatString(data.Domain), + 213: resources_core.FormatInt64(data.ResourceCPU), + 214: resources_core.FormatInt64(data.ResourceMemory), + 215: resources_core.FormatInt64(data.ResourceInstances), + 216: resources_core.FormatString(data.ResourceRealm), + 217: resources_core.FormatString(data.RegistryPath), + 241: resources_core.FormatString(data.JsonEnv), + 782: resources_core.FormatString(data.IngressAnnotations), + } + + operationTimeout := "" + if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { + operationTimeout = data.OperationTimeout.ValueString() + } + if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) + } + id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + data.ID = types.StringValue(id) + + state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 98, data, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "registryPath", Field: "RegistryPath", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HttpResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state HttpModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + if r.client != nil { + remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + if remove { + resp.State.RemoveResource(ctx) + return + } + } + + newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 98, state, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "registryPath", Field: "RegistryPath", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +func (r *HttpResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan HttpModel + var state HttpModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + instanceID := state.ID + if instanceID.IsNull() || instanceID.IsUnknown() { + instanceID = plan.ID + } + if instanceID.IsNull() || instanceID.IsUnknown() { + resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") + return + } + + hasServiceParamChanges := false + if !hasServiceParamChanges { + if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { + if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { + if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { + hasServiceParamChanges = true + } + } + } + if !hasServiceParamChanges { + if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { + hasServiceParamChanges = true + } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { + if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { + hasServiceParamChanges = true + } + } + } + // --- Redeploy support (ARCHITECTURE.md) --- + // Проверяем изменился ли git_revision. + // Если да — вызываем redeploy вместо modify. + // git_revision = "" (не задано) → обычный modify. + redeployRequested := false + if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { + if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { + redeployRequested = true + } + } + + if !hasServiceParamChanges && !redeployRequested { + plan.ID = instanceID + plan.StateParams = state.StateParams + plan.StateOut = state.StateOut + plan.StateParamsFlat = state.StateParamsFlat + plan.StateOutFlat = state.StateOutFlat + plan.VaultSecrets = state.VaultSecrets + plan.VaultUrl = state.VaultUrl + plan.VaultUserPath = state.VaultUserPath + plan.VaultFields = state.VaultFields + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + return + } + + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } + // --- Redeploy support (ARCHITECTURE.md) --- + if redeployRequested { + // Вызываем redeploy — пустой params (операция не требует CFS-параметров) + if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } else { + params := map[int]string{ + 218: resources_core.FormatInt64(plan.ResourceCPU), + 219: resources_core.FormatInt64(plan.ResourceMemory), + 220: resources_core.FormatInt64(plan.ResourceInstances), + } + if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } + } + + plan.ID = instanceID + state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 98, plan, []resources_core.StateField{ + {Code: "state_params"}, + {Code: "state_out"}, + {Code: "state_params_flat"}, + {Code: "state_out_flat"}, + {Code: "vault_secrets"}, + {Code: "vault_url"}, + {Code: "vault_user_path"}, + {Code: "vault_fields"}, + }, []resources_core.InputField{ + {Code: "domain", Field: "Domain", Type: "string"}, + {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, + {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, + {Code: "registryPath", Field: "RegistryPath", Type: "string"}, + {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, + {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, + {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, + {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, + }) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *HttpResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state HttpModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + if state.ID.IsNull() || state.ID.IsUnknown() { + return + } + deleteMode := "state_only" + if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { + deleteMode = "suspend" + } + + operationTimeout := "" + if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { + operationTimeout = state.OperationTimeout.ValueString() + } + if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) + } + if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { + resp.Diagnostics.AddError("Ошибка клиента", err.Error()) + return + } +} + +func (r *HttpResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *HttpResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*core.UniversalClient) + if !ok { + resp.Diagnostics.AddError("Error", "Invalid client type") + return + } + r.client = client +} diff --git a/provider/internal/resources_gen/99_gitea_resource.go b/generated/test/go/99_gitea_resource.go similarity index 99% rename from provider/internal/resources_gen/99_gitea_resource.go rename to generated/test/go/99_gitea_resource.go index 8848384..954de53 100644 --- a/provider/internal/resources_gen/99_gitea_resource.go +++ b/generated/test/go/99_gitea_resource.go @@ -465,6 +465,13 @@ func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, return } + operationTimeout := "" + if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { + operationTimeout = plan.OperationTimeout.ValueString() + } + if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { + ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) + } params := map[int]string{ 259: resources_core.FormatInt64(plan.ResourceCPU), 260: resources_core.FormatInt64(plan.ResourceMemory), @@ -473,14 +480,6 @@ func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, 819: resources_core.FormatString(plan.IngressExtraAnnotations), 820: resources_core.FormatString(plan.IniServerParams), } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { resp.Diagnostics.AddError("Ошибка клиента", err.Error()) return diff --git a/generated/test/go/registry.go b/generated/test/go/registry.go new file mode 100644 index 0000000..58a4539 --- /dev/null +++ b/generated/test/go/registry.go @@ -0,0 +1,67 @@ +package resources_gen + +import "github.com/hashicorp/terraform-plugin-framework/resource" + +// Code generated by tools/gen_v2. DO NOT EDIT. +func AllResources() []func() resource.Resource { + return []func() resource.Resource{ + NewAkhqResource, + NewClickhouseResource, + NewDnsrecordResource, + NewDnszoneResource, + NewDummyResource, + NewFlaskResource, + NewGiteaResource, + NewGiteaComplexResource, + NewHarborResource, + NewHttpResource, + NewK8sSthutrvalClusterResource, + NewK8sZitiControllerResource, + NewKafkaResource, + NewLuceeResource, + NewMariadbResource, + NewMongodbResource, + NewNextcloudResource, + NewNifiResource, + NewNodejsResource, + NewNoderedResource, + NewOpenwhiskResource, + NewPgadminResource, + NewPostgresResource, + NewRabbitmqResource, + NewRedisResource, + NewS3Resource, + NewS3bucketResource, + NewSupersetResource, + NewTemplateResource, + NewTenantResource, + NewValoTenantResource, + NewVappResource, + NewVcComplexResource, + NewVcNsxtResource, + NewVcOrgResource, + NewVcOrgSaasResource, + NewVcVdcResource, + NewVcVdcGroupResource, + NewVcVmResource, + NewVcVmV2Resource, + NewVcVmV3Resource, + NewVcexternalipResource, + NewVmpostgreResource, + NewClickhouseDatabaseResource, + NewClickhouseUserResource, + NewK8sSthutrvalClusterUserResource, + NewKafkaTopicResource, + NewKafkaUserResource, + NewMariadbDatabaseResource, + NewMariadbUserResource, + NewMongodbUserResource, + NewNextcloudBackupResource, + NewPostgresBackupResource, + NewPostgresDatabaseResource, + NewPostgresUserResource, + NewS3SubUserResource, + NewVcOrgUserResource, + NewVcVdcGroupVdcResource, + } +} diff --git a/generated/test/json/.gitkeep b/generated/test/json/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/generated/test/output_inventory/index.json b/generated/test/output_inventory/index.json new file mode 100644 index 0000000..a5c66ca --- /dev/null +++ b/generated/test/output_inventory/index.json @@ -0,0 +1,496 @@ +[ + { + "serviceId": 110, + "svc_code": "DNS зона", + "instance_count": 2, + "state_params_keys": [], + "state_out_keys": [], + "vault_keys": [] + }, + { + "serviceId": 111, + "svc_code": "DNS запись", + "instance_count": 5, + "state_params_keys": [ + "recordInput", + "recordName", + "recordTTL", + "recordType", + "zoneUid" + ], + "state_out_keys": [ + "dnsServers", + "record" + ], + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "serviceId": 112, + "svc_code": "Тенант в Grafana", + "instance_count": 1, + "state_params_keys": [ + "resourceRealm" + ], + "state_out_keys": [ + "connectionUrl", + "monitoring" + ], + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "serviceId": 116, + "svc_code": "ApacheKafka", + "instance_count": 2, + "state_params_keys": [], + "state_out_keys": [], + "vault_keys": [] + }, + { + "serviceId": 12, + "svc_code": "S3 Object Storage", + "instance_count": 1, + "state_params_keys": [ + "maxBucketsPerUser", + "maxObjectsPerBucket", + "maxSizeGbPerUser", + "resourceRealm", + "userName" + ], + "state_out_keys": [ + "dtEndTrial", + "dtStartTrial", + "isTrial", + "monitoring", + "urlConnect" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 13, + "svc_code": "S3 бакет", + "instance_count": 6, + "state_params_keys": [ + "bucketName", + "corsAll", + "listAll", + "maxSize", + "placement", + "readAll", + "s3UserUid" + ], + "state_out_keys": [ + "httpConnect" + ], + "vault_keys": [] + }, + { + "serviceId": 150, + "svc_code": "Kubernetes кластер Штурвал", + "instance_count": 1, + "state_params_keys": [ + "access", + "accessConfiguration", + "clusterConfiguration", + "controlPlaneConfiguration", + "startupConfiguration", + "workerConfiguration" + ], + "state_out_keys": [ + "ingressAddress", + "kubernetesApiAddress", + "webUrl" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 19, + "svc_code": "Организация в Cloud Director", + "instance_count": 1, + "state_params_keys": [ + "organizationType", + "resourceRealm", + "vIPConfigure" + ], + "state_out_keys": [ + "urlConnect" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 1, + "svc_code": "Болванка", + "instance_count": 1, + "state_params_keys": [], + "state_out_keys": [], + "vault_keys": [] + }, + { + "serviceId": 21, + "svc_code": "Виртуальный датацентр (vDC)", + "instance_count": 1, + "state_params_keys": [ + "cpuAllocated", + "cpuGuaranteed", + "memAllocated", + "memGuaranteed", + "networkProvider", + "organizationUid", + "providerVdc", + "storageConfig" + ], + "state_out_keys": [ + "sizingPolicies", + "vdcName" + ], + "vault_keys": [] + }, + { + "serviceId": 22, + "svc_code": "Сетевой шлюз периметра (Edge)", + "instance_count": 1, + "state_params_keys": [ + "ipSpaceName", + "needEnableAVI", + "needExternalAddressSNAT", + "vdcGroupUid", + "vdcType", + "vdcUid", + "virtualServicesCount" + ], + "state_out_keys": [ + "nsxName", + "routedNet", + "vdcName" + ], + "vault_keys": [] + }, + { + "serviceId": 25, + "svc_code": "Публичные IP адреса", + "instance_count": 2, + "state_params_keys": [ + "dnatCreate", + "fromServiceCloudEdgeName", + "fromServiceCloudEdgeScope", + "fromServiceCloudOrgName", + "fromServiceCloudVdcName", + "fromServiceCloudVmwareUrl", + "fromServiceNamespace", + "fromServiceVdcGroupName", + "internalAddrAccess", + "internalPortAccess", + "ipSpaceName", + "resourceRealm", + "serviceUid", + "snatCreate" + ], + "state_out_keys": [ + "externalIp" + ], + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "serviceId": 26, + "svc_code": "Виртуальный каталог ВМ (vApp)", + "instance_count": 2, + "state_params_keys": [ + "nsxtUid", + "vappName", + "vdcUid" + ], + "state_out_keys": [], + "vault_keys": [] + }, + { + "serviceId": 28, + "svc_code": "Виртуальная машина", + "instance_count": 2, + "state_params_keys": [ + "accessIpList", + "accessPortList", + "cloudInit", + "imageVm", + "ipSpaceName", + "userLogin", + "userPublicKey", + "vappUid", + "vmCpu", + "vmDisk", + "vmName", + "vmRam" + ], + "state_out_keys": [ + "externalConnect", + "fqdn", + "internalConnect", + "monitoring" + ], + "vault_keys": [] + }, + { + "serviceId": 81, + "svc_code": "Apache Superset", + "instance_count": 2, + "state_params_keys": [], + "state_out_keys": [], + "vault_keys": [] + }, + { + "serviceId": 89, + "svc_code": "Web-сервер с фреймворком Flask", + "instance_count": 4, + "state_params_keys": [ + "domain", + "gitPath", + "healthPath", + "jsonEnv", + "resourceCPU", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "connectionUrl", + "monitoring" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 90, + "svc_code": "PostgreSQL", + "instance_count": 3, + "state_params_keys": [ + "allowNoSSL", + "appVersion", + "autoScale", + "autoScalePercentage", + "autoScaleQuotaGb", + "autoScaleTechWindow", + "enablePgPoolerMaster", + "enablePgPoolerSlave", + "ext_BACKUP_NUM_TO_RETAIN", + "ext_BACKUP_SCHEDULE", + "ipSpaceNameMaster", + "ipSpaceNameSlave", + "jsonParameters", + "needExternalAddressMaster", + "needExternalAddressSlave", + "resourceCPU", + "resourceDisk", + "resourceInstances", + "resourceMemory", + "resourceRealm", + "s3Uid" + ], + "state_out_keys": [ + "databases", + "externalConnect", + "internalConnect", + "monitoring", + "users" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 91, + "svc_code": "Redis", + "instance_count": 3, + "state_params_keys": [ + "needExternalAddressMaster", + "needExternalAddressSlave", + "resourceCPU", + "resourceDisk", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "externalConnect", + "internalConnect", + "monitoring" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 92, + "svc_code": "MongoDB", + "instance_count": 2, + "state_params_keys": [ + "needExternalAddressMaster", + "resourceCPU", + "resourceDisk", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "internalConnect", + "monitoring" + ], + "vault_keys": [] + }, + { + "serviceId": 93, + "svc_code": "RabbitMQ", + "instance_count": 1, + "state_params_keys": [ + "needExternalAddressMaster", + "resourceCPU", + "resourceDisk", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "clusterDomain", + "externalConnect", + "inernalConnect", + "monitoring" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 94, + "svc_code": "Lucee", + "instance_count": 1, + "state_params_keys": [ + "appVersion", + "domain", + "gitPath", + "healthPath", + "jsonEnv", + "resourceCPU", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "connectionUrl", + "monitoring" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 95, + "svc_code": "NodeJS", + "instance_count": 1, + "state_params_keys": [ + "appVersion", + "domain", + "gitPath", + "healthPath", + "jsonEnv", + "resourceCPU", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "connectionUrl", + "monitoring" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 96, + "svc_code": "pgAdmin", + "instance_count": 3, + "state_params_keys": [ + "domain", + "login", + "password", + "resourceCPU", + "resourceDisk", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "connectionUrl", + "monitoring" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + }, + { + "serviceId": 97, + "svc_code": "NodeRed", + "instance_count": 4, + "state_params_keys": [ + "resourceRealm" + ], + "state_out_keys": [], + "vault_keys": [] + }, + { + "serviceId": 98, + "svc_code": "Простой HTTP контейнер", + "instance_count": 2, + "state_params_keys": [ + "domain", + "jsonEnv", + "registryPath", + "resourceCPU", + "resourceInstances", + "resourceMemory", + "resourceRealm" + ], + "state_out_keys": [ + "connectionUrl", + "monitoring" + ], + "vault_keys": [ + "fields", + "url", + "userPath" + ] + } +] \ No newline at end of file diff --git a/generated/test/output_inventory/svc_110_DNS_зона.json b/generated/test/output_inventory/svc_110_DNS_зона.json new file mode 100644 index 0000000..4888ed9 --- /dev/null +++ b/generated/test/output_inventory/svc_110_DNS_зона.json @@ -0,0 +1,54 @@ +{ + "serviceId": 110, + "svc_code": "DNS зона", + "instances": [ + { + "uid": "CF5863D1-1BE7-46DA-992C-904A3A76979E", + "name": "inv-svc-110-r4", + "serviceId": 110, + "svc_code": "DNS зона", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "3F5695A1-6CFA-4213-867B-EA9170566D35", + "name": "inv-svc-110-r5", + "serviceId": 110, + "svc_code": "DNS зона", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "094053f9-0acb-49c8-89da-e09e7a06f832", + "name": "supermax", + "serviceId": 110, + "svc_code": "DNS зона", + "status": "running", + "source": "prod", + "params": { + "zoneTtl": "86400", + "zoneKind": "NATIVE", + "zoneName": "k8c.ru", + "zoneEmail": "tazet@narod.ru", + "zoneRetry": "7200", + "zoneExpire": "3600000", + "zoneRefresh": "86400", + "resourceRealm": "powerdns", + "zoneMastersForSlave": "" + }, + "out": { + "dnsServers": [ + "185.247.187.83", + "81.22.46.43" + ], + "zoneSerial": "2025102601", + "zoneSoaServer": "ns.ngcloud.ru" + }, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_111_DNS_запись.json b/generated/test/output_inventory/svc_111_DNS_запись.json new file mode 100644 index 0000000..db469f2 --- /dev/null +++ b/generated/test/output_inventory/svc_111_DNS_запись.json @@ -0,0 +1,121 @@ +{ + "serviceId": 111, + "svc_code": "DNS запись", + "instances": [ + { + "uid": "cbd153bd-5d17-42f3-acc2-d0cfdf75c036", + "name": "A-record (web-test-stand.luceek8s)", + "status": "running", + "params": { + "zoneUid": "4398ca1b-e96d-4829-90b7-d7eb0e798389", + "recordTTL": "300", + "recordName": "web-test-stand.luceek8s", + "recordType": "A", + "recordInput": "91.214.116.222" + }, + "out": { + "record": "web-test-stand.luceek8s.dev.nubes.ru", + "dnsServers": [ + "185.247.187.83", + "81.22.46.43" + ] + }, + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "uid": "97b4ab52-74b9-4146-955f-d855b878b376", + "name": "A-record (web02.vapp-sless.a9f8779d-4b8f-40be-bcaf-9427fab6ce4b)", + "status": "running", + "params": { + "zoneUid": "4398ca1b-e96d-4829-90b7-d7eb0e798389", + "recordTTL": "300", + "recordName": "web02.vapp-sless.a9f8779d-4b8f-40be-bcaf-9427fab6ce4b", + "recordType": "A", + "recordInput": "185.247.187.154" + }, + "out": { + "record": "web02.vapp-sless.a9f8779d-4b8f-40be-bcaf-9427fab6ce4b.dev.nubes.ru", + "dnsServers": [ + "185.247.187.83", + "81.22.46.43" + ] + }, + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "uid": "d4d7c97f-efa8-4885-b919-8c037c20524a", + "name": "A-record (node07.nodejsk8s)", + "status": "running", + "params": { + "zoneUid": "4398ca1b-e96d-4829-90b7-d7eb0e798389", + "recordTTL": "300", + "recordName": "node07.nodejsk8s", + "recordType": "A", + "recordInput": "91.214.116.222" + }, + "out": { + "record": "node07.nodejsk8s.dev.nubes.ru", + "dnsServers": [ + "185.247.187.83", + "81.22.46.43" + ] + }, + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "uid": "ad471cd8-1b2e-4747-af41-b4b730ed846e", + "name": "A-record (baogg.openbaok8s)", + "status": "running", + "params": { + "zoneUid": "4398ca1b-e96d-4829-90b7-d7eb0e798389", + "recordTTL": "300", + "recordName": "baogg.openbaok8s", + "recordType": "A", + "recordInput": "91.214.116.222" + }, + "out": { + "record": "baogg.openbaok8s.dev.nubes.ru", + "dnsServers": [ + "185.247.187.83", + "81.22.46.43" + ] + }, + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "uid": "9fa09e8c-2725-4efa-ace6-83123ed5ebfc", + "name": "A-record (admin.1dbfe9da-ce1c-4958-b359-d016a4b455c8)", + "status": "running", + "params": { + "zoneUid": "4398ca1b-e96d-4829-90b7-d7eb0e798389", + "recordTTL": "300", + "recordName": "admin.1dbfe9da-ce1c-4958-b359-d016a4b455c8", + "recordType": "A", + "recordInput": "185.247.187.147" + }, + "out": { + "record": "admin.1dbfe9da-ce1c-4958-b359-d016a4b455c8.dev.nubes.ru", + "dnsServers": [ + "185.247.187.83", + "81.22.46.43" + ] + }, + "vault_keys": [ + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_112_Тенант_в_Grafana.json b/generated/test/output_inventory/svc_112_Тенант_в_Grafana.json new file mode 100644 index 0000000..cf661fc --- /dev/null +++ b/generated/test/output_inventory/svc_112_Тенант_в_Grafana.json @@ -0,0 +1,30 @@ +{ + "serviceId": 112, + "svc_code": "Тенант в Grafana", + "instances": [ + { + "uid": "19696eb9-5af5-4e0a-ad00-f49a10d755d4", + "name": "Grafana Tenant (WZ03709)", + "status": "running", + "params": { + "resourceRealm": "grafana.ngcloud.ru" + }, + "out": { + "monitoring": { + "pdu": { + "url": "https://grafana.ngcloud.ru/d/19696eb9-5af5-4e0a-ad00-f49a10d755d4-pd3?orgId=962" + }, + "vms": { + "url": "https://grafana.ngcloud.ru/d/19696eb9-5af5-4e0a-ad00-f49a10d755d4-vm2?orgId=962" + }, + "resourceMetrics": "https://grafana.ngcloud.ru/dashboards/f/19696eb9-5af5-4e0a-ad00-f49a10d755d4?orgId=962" + }, + "connectionUrl": "https://grafana.ngcloud.ru/?orgId=962" + }, + "vault_keys": [ + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_116_ApacheKafka.json b/generated/test/output_inventory/svc_116_ApacheKafka.json new file mode 100644 index 0000000..97a16d5 --- /dev/null +++ b/generated/test/output_inventory/svc_116_ApacheKafka.json @@ -0,0 +1,120 @@ +{ + "serviceId": 116, + "svc_code": "ApacheKafka", + "instances": [ + { + "uid": "71763169-2053-420E-8CFF-69410E41C978", + "name": "inv-svc-116-1774957184", + "serviceId": 116, + "svc_code": "ApacheKafka", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "DA96A991-E8B7-43C0-8C97-9947A6A0C333", + "name": "inv-svc-116-r2", + "serviceId": 116, + "svc_code": "ApacheKafka", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "bd1d689e-bce2-4792-9f1f-73cd1d6c7de7", + "name": "tf-kafka-20260203-01", + "serviceId": 116, + "svc_code": "ApacheKafka", + "status": "running", + "source": "prod", + "params": { + "resourceCPU": 1000, + "resourceDisk": "1", + "resourceRealm": "k8s-3.ext.nubes.ru", + "resourceMemory": 1000, + "ipSpaceNameMaster": "", + "resourceInstances": 1, + "needExternalAddressMaster": "false" + }, + "out": { + "monitoring": { + "base": { + "url": "https://grafana.ngcloud.ru/d/bd1d689e-bce2-4792-9f1f-73cd1d6c7de7-def?orgId=28" + }, + "loki": { + "url": "https://grafana.ngcloud.ru/d/bd1d689e-bce2-4792-9f1f-73cd1d6c7de7-log?orgId=28" + }, + "allDashboards": "https://grafana.ngcloud.ru/dashboards/f/bd1d689e-bce2-4792-9f1f-73cd1d6c7de7?orgId=28" + }, + "externalConnect": { + "bootstrap": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + } + }, + "internalConnect": "kafkak8s-kafka-bootstrap.bd1d689e-bce2-4792-9f1f-73cd1d6c7de7.svc.k8s-3.ext.nubes.ru" + }, + "vault_keys": [] + }, + { + "uid": "18257650-5edd-40e9-bcd9-98c85add3a19", + "name": "kafka-2", + "serviceId": 116, + "svc_code": "ApacheKafka", + "status": "suspended", + "source": "prod", + "params": { + "resourceCPU": 2000, + "resourceDisk": "10", + "resourceRealm": "k8s-3.ext.nubes.ru", + "resourceMemory": 2048, + "ipSpaceNameMaster": "internet-no-antiddos-v1", + "resourceInstances": 2, + "needExternalAddressMaster": false + }, + "out": { + "kafkaUsers": { + "usertest": { + "group": "*", + "nameTopic": "*", + "operations": "Create, Describe, Read, Write", + "accessHosts": "*" + } + }, + "monitoring": { + "base": { + "url": "https://grafana.ngcloud.ru/d/18257650-5edd-40e9-bcd9-98c85add3a19-def?orgId=28" + }, + "loki": { + "url": "https://grafana.ngcloud.ru/d/18257650-5edd-40e9-bcd9-98c85add3a19-log?orgId=28" + }, + "allDashboards": "https://grafana.ngcloud.ru/dashboards/f/18257650-5edd-40e9-bcd9-98c85add3a19?orgId=28" + }, + "kafkaTopics": { + "test-events": { + "replicas": "2", + "partitions": "6" + } + }, + "externalConnect": { + "bootstrap": { + "ip": "", + "fqdn": "", + "uuid": "7defcbcc-543e-4967-b83d-8bdd3b4592ae", + "isExternal": false + } + }, + "internalConnect": "kafkak8s-kafka-bootstrap.18257650-5edd-40e9-bcd9-98c85add3a19.svc.k8s-3.ext.nubes.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_119_Akhq.json b/generated/test/output_inventory/svc_119_Akhq.json new file mode 100644 index 0000000..ce1b49d --- /dev/null +++ b/generated/test/output_inventory/svc_119_Akhq.json @@ -0,0 +1,29 @@ +{ + "serviceId": 119, + "svc_code": "Akhq", + "instances": [ + { + "uid": "305707e5-2e64-4bfa-ad41-3141e3449791", + "name": "Akhq-4", + "serviceId": 119, + "svc_code": "Akhq", + "status": "suspended", + "source": "prod", + "params": { + "domain": "", + "resourceCPU": "", + "resourceRealm": "", + "resourceMemory": "", + "resourceInstances": "" + }, + "out": { + "connectionUrl": "https://akhq2002.akhqk8s.services.ngcloud.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_12_S3_Object_Storage.json b/generated/test/output_inventory/svc_12_S3_Object_Storage.json new file mode 100644 index 0000000..88fa6ca --- /dev/null +++ b/generated/test/output_inventory/svc_12_S3_Object_Storage.json @@ -0,0 +1,35 @@ +{ + "serviceId": 12, + "svc_code": "S3 Object Storage", + "instances": [ + { + "uid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "name": "naeel-s3", + "status": "running", + "params": { + "userName": "naeels3", + "resourceRealm": "ceph.tst.nubes.ru", + "maxSizeGbPerUser": "1024", + "maxBucketsPerUser": 25, + "maxObjectsPerBucket": "10000000" + }, + "out": { + "isTrial": true, + "dtEndTrial": "2026-03-27T18:16:11.026+0300", + "monitoring": { + "s3ceph": { + "url": "https://grafana.ngcloud.ru/d/332cdb0d-34bf-43bf-864d-4adcc3b556fb-s3?orgId=962" + }, + "resourceMetrics": "https://grafana.ngcloud.ru/dashboards/f/332cdb0d-34bf-43bf-864d-4adcc3b556fb?orgId=962" + }, + "urlConnect": "http://ceph.tst.nubes.ru", + "dtStartTrial": "2026-03-13T18:16:11.026+0300" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_13_S3_бакет.json b/generated/test/output_inventory/svc_13_S3_бакет.json new file mode 100644 index 0000000..09411a0 --- /dev/null +++ b/generated/test/output_inventory/svc_13_S3_бакет.json @@ -0,0 +1,114 @@ +{ + "serviceId": 13, + "svc_code": "S3 бакет", + "instances": [ + { + "uid": "37f35a6b-ec3e-404b-b8aa-16d43317fd79", + "name": "S3-Bucket (technicals3backup-f97978e3-9300-41bb-b22c-4f9aecf674f8)", + "status": "running", + "params": { + "corsAll": "false", + "listAll": "false", + "maxSize": "-1", + "readAll": "false", + "placement": "COLD", + "s3UserUid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "bucketName": "technicals3backup-f97978e3-9300-41bb-b22c-4f9aecf674f8" + }, + "out": { + "httpConnect": "http://ceph.tst.nubes.ru/technicals3backup-f97978e3-9300-41bb-b22c-4f9aecf674f8" + }, + "vault_keys": [] + }, + { + "uid": "bc401b9c-6b55-466c-b6e5-85131b69ecf9", + "name": "S3-Bucket (technicals3backup-f87f606f-b32e-4eb2-9563-8c3bf88a2d82)", + "status": "running", + "params": { + "corsAll": "false", + "listAll": "false", + "maxSize": "-1", + "readAll": "false", + "placement": "COLD", + "s3UserUid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "bucketName": "technicals3backup-f87f606f-b32e-4eb2-9563-8c3bf88a2d82" + }, + "out": { + "httpConnect": "http://ceph.tst.nubes.ru/technicals3backup-f87f606f-b32e-4eb2-9563-8c3bf88a2d82" + }, + "vault_keys": [] + }, + { + "uid": "99260e62-4e05-4f2f-ad4c-217d79ef8f1f", + "name": "S3-Bucket (technicals3backup-bba55e0f-4c51-4312-8ad3-efc557de331d)", + "status": "running", + "params": { + "corsAll": "false", + "listAll": "false", + "maxSize": "-1", + "readAll": "false", + "placement": "COLD", + "s3UserUid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "bucketName": "technicals3backup-bba55e0f-4c51-4312-8ad3-efc557de331d" + }, + "out": { + "httpConnect": "http://ceph.tst.nubes.ru/technicals3backup-bba55e0f-4c51-4312-8ad3-efc557de331d" + }, + "vault_keys": [] + }, + { + "uid": "c7b50a72-ef23-40c8-86c0-a6c07883444f", + "name": "S3-Bucket (technicals3backup-99dd3428-7a98-453c-a7a3-9930bc7af299)", + "status": "running", + "params": { + "corsAll": "false", + "listAll": "false", + "maxSize": "-1", + "readAll": "false", + "placement": "COLD", + "s3UserUid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "bucketName": "technicals3backup-99dd3428-7a98-453c-a7a3-9930bc7af299" + }, + "out": { + "httpConnect": "http://ceph.tst.nubes.ru/technicals3backup-99dd3428-7a98-453c-a7a3-9930bc7af299" + }, + "vault_keys": [] + }, + { + "uid": "c1a622ba-50a9-4344-ae5f-1b32c38cb344", + "name": "poc-s3evt-src", + "status": "running", + "params": { + "corsAll": "false", + "listAll": "false", + "maxSize": "-1", + "readAll": "false", + "placement": "HOT", + "s3UserUid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "bucketName": "poc-s3evt-src" + }, + "out": { + "httpConnect": "http://ceph.tst.nubes.ru/poc-s3evt-src" + }, + "vault_keys": [] + }, + { + "uid": "c1a622ba-50a9-4344-ae5f-1b32c38cb344", + "name": "poc-s3evt-src", + "status": "running", + "params": { + "corsAll": "false", + "listAll": "false", + "maxSize": "-1", + "readAll": "false", + "placement": "HOT", + "s3UserUid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "bucketName": "poc-s3evt-src" + }, + "out": { + "httpConnect": "http://ceph.tst.nubes.ru/poc-s3evt-src" + }, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_150_Kubernetes_кластер_Штурвал.json b/generated/test/output_inventory/svc_150_Kubernetes_кластер_Штурвал.json new file mode 100644 index 0000000..cbf972b --- /dev/null +++ b/generated/test/output_inventory/svc_150_Kubernetes_кластер_Штурвал.json @@ -0,0 +1,56 @@ +{ + "serviceId": 150, + "svc_code": "Kubernetes кластер Штурвал", + "instances": [ + { + "uid": "0fcbae6c-d4b5-4f53-8619-a59fcff1d0b5", + "name": "naeel-wheel", + "status": "running", + "params": { + "access": { + "accessIpListAPI": "[]", + "accessIpListIngress": "[]", + "needExternalAddressAPI": "true", + "needExternalAddressIngress": "true" + }, + "accessConfiguration": { + "accessIpListAPI": "[]", + "accessIpListIngress": "[]", + "needExternalAddressAPI": "true", + "needExternalAddressIngress": "true" + }, + "workerConfiguration": [ + { + "count": "3", + "groupName": "workers", + "sizingDisk": "50", + "sizingPolicy": "TKG 4CPU 8RAM" + } + ], + "clusterConfiguration": { + "appVersion": "2.12.1" + }, + "startupConfiguration": { + "vdcUid": "e3c9e4f1-24da-4992-a003-f8a2a803a5f0", + "nsxtUid": "0fe88e2a-31b6-4385-ad52-e27c6c0d38a6", + "clusterName": "naeel-test-3" + }, + "controlPlaneConfiguration": { + "count": "3", + "sizingDisk": "50", + "sizingPolicy": "TKG 4CPU 8RAM" + } + }, + "out": { + "webUrl": "https://k8s.ngcloud.ru/clusters/naeel-test-3/dashboard", + "ingressAddress": "185.247.187.147", + "kubernetesApiAddress": "185.247.187.146" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_19_Организация_в_Cloud_Director.json b/generated/test/output_inventory/svc_19_Организация_в_Cloud_Director.json new file mode 100644 index 0000000..b2daf66 --- /dev/null +++ b/generated/test/output_inventory/svc_19_Организация_в_Cloud_Director.json @@ -0,0 +1,29 @@ +{ + "serviceId": 19, + "svc_code": "Организация в Cloud Director", + "instances": [ + { + "uid": "3f0850f2-3506-4efd-b84b-7270b5027ab5", + "name": "NaeelOrg", + "status": "running", + "params": { + "vIPConfigure": [ + { + "name": "internet-ipv4-v1", + "count": "10" + } + ], + "resourceRealm": "sandbox.nubes.ru", + "organizationType": "iaas" + }, + "out": { + "urlConnect": "https://sandbox.nubes.ru/tenant/WZ03709-iaas/" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_1_Болванка.json b/generated/test/output_inventory/svc_1_Болванка.json new file mode 100644 index 0000000..f02d7d6 --- /dev/null +++ b/generated/test/output_inventory/svc_1_Болванка.json @@ -0,0 +1,16 @@ +{ + "serviceId": 1, + "svc_code": "Болванка", + "instances": [ + { + "uid": "94B28619-7FA8-4B51-AB2F-DC8E5CE4A02C", + "name": "inv-dummy-1774955714", + "serviceId": 1, + "svc_code": "Болванка", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_21_Виртуальный_датацентр_(vDC).json b/generated/test/output_inventory/svc_21_Виртуальный_датацентр_(vDC).json new file mode 100644 index 0000000..eff792c --- /dev/null +++ b/generated/test/output_inventory/svc_21_Виртуальный_датацентр_(vDC).json @@ -0,0 +1,36 @@ +{ + "serviceId": 21, + "svc_code": "Виртуальный датацентр (vDC)", + "instances": [ + { + "uid": "e3c9e4f1-24da-4992-a003-f8a2a803a5f0", + "name": "naeel-vdc", + "status": "running", + "params": { + "providerVdc": "Intel Broadwell 2.4", + "cpuAllocated": "40", + "memAllocated": "100", + "cpuGuaranteed": "20", + "memGuaranteed": "20", + "storageConfig": [ + { + "name": "SSD", + "size": "2000" + } + ], + "networkProvider": "snb1", + "organizationUid": "3f0850f2-3506-4efd-b84b-7270b5027ab5" + }, + "out": { + "vdcName": "WZ03709-iaas-sandbox-v1cl1-pvdc-ywmmd", + "sizingPolicies": [ + "TKG 16CPU 32RAM", + "TKG 2CPU 4RAM", + "TKG 4CPU 8RAM", + "TKG 8CPU 16RAM" + ] + }, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_22_Сетевой_шлюз_периметра_(Edge).json b/generated/test/output_inventory/svc_22_Сетевой_шлюз_периметра_(Edge).json new file mode 100644 index 0000000..aaf5574 --- /dev/null +++ b/generated/test/output_inventory/svc_22_Сетевой_шлюз_периметра_(Edge).json @@ -0,0 +1,26 @@ +{ + "serviceId": 22, + "svc_code": "Сетевой шлюз периметра (Edge)", + "instances": [ + { + "uid": "0fe88e2a-31b6-4385-ad52-e27c6c0d38a6", + "name": "naeel_vc_nsxt", + "status": "running", + "params": { + "vdcUid": "e3c9e4f1-24da-4992-a003-f8a2a803a5f0", + "vdcType": "vdc", + "ipSpaceName": "internet-ipv4-v1", + "vdcGroupUid": "", + "needEnableAVI": true, + "virtualServicesCount": "4", + "needExternalAddressSNAT": true + }, + "out": { + "nsxName": "nsx_WZ03709-iaas-h67go75t", + "vdcName": "WZ03709-iaas-sandbox-v1cl1-pvdc-ywmmd", + "routedNet": "routed_WZ03709-iaas-htpse6fu" + }, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_25_Публичные_IP_адреса.json b/generated/test/output_inventory/svc_25_Публичные_IP_адреса.json new file mode 100644 index 0000000..9f0c93a --- /dev/null +++ b/generated/test/output_inventory/svc_25_Публичные_IP_адреса.json @@ -0,0 +1,66 @@ +{ + "serviceId": 25, + "svc_code": "Публичные IP адреса", + "instances": [ + { + "uid": "1b613370-3aef-4034-9840-859af1223d85", + "name": "vIp for (a9f8779d-4b8f-40be-bcaf-9427fab6ce4b)", + "status": "running", + "params": { + "dnatCreate": "true", + "serviceUid": "a9f8779d-4b8f-40be-bcaf-9427fab6ce4b", + "snatCreate": "true", + "ipSpaceName": "internet-ipv4-v1", + "resourceRealm": "sandbox.nubes.ru", + "internalAddrAccess": "10.10.102.6", + "internalPortAccess": [ + "all" + ], + "fromServiceNamespace": "web02", + "fromServiceCloudOrgName": "WZ03709-iaas", + "fromServiceCloudVdcName": "WZ03709-iaas-sandbox-v1cl1-pvdc-ywmmd", + "fromServiceVdcGroupName": "", + "fromServiceCloudEdgeName": "nsx_WZ03709-iaas-h67go75t", + "fromServiceCloudEdgeScope": "vdc", + "fromServiceCloudVmwareUrl": "https://sandbox.nubes.ru" + }, + "out": { + "externalIp": "185.247.187.154" + }, + "vault_keys": [ + "fields", + "userPath" + ] + }, + { + "uid": "d9b02475-f23a-4316-8d75-4a684c58fed9", + "name": "vIp for (0fe88e2a-31b6-4385-ad52-e27c6c0d38a6)", + "status": "running", + "params": { + "dnatCreate": "false", + "serviceUid": "0fe88e2a-31b6-4385-ad52-e27c6c0d38a6", + "snatCreate": "true", + "ipSpaceName": "internet-ipv4-v1", + "resourceRealm": "sandbox.nubes.ru", + "internalAddrAccess": "10.10.102.0/24", + "internalPortAccess": [ + "all" + ], + "fromServiceNamespace": "master", + "fromServiceCloudOrgName": "WZ03709-iaas", + "fromServiceCloudVdcName": "WZ03709-iaas-sandbox-v1cl1-pvdc-ywmmd", + "fromServiceVdcGroupName": "", + "fromServiceCloudEdgeName": "nsx_WZ03709-iaas-h67go75t", + "fromServiceCloudEdgeScope": "vdc", + "fromServiceCloudVmwareUrl": "https://sandbox.nubes.ru" + }, + "out": { + "externalIp": "185.247.187.140" + }, + "vault_keys": [ + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_26_Виртуальный_каталог_ВМ_(vApp).json b/generated/test/output_inventory/svc_26_Виртуальный_каталог_ВМ_(vApp).json new file mode 100644 index 0000000..8fd6364 --- /dev/null +++ b/generated/test/output_inventory/svc_26_Виртуальный_каталог_ВМ_(vApp).json @@ -0,0 +1,30 @@ +{ + "serviceId": 26, + "svc_code": "Виртуальный каталог ВМ (vApp)", + "instances": [ + { + "uid": "f037ddea-fc1d-46c5-9eef-577e07878c9c", + "name": "vm-sless-vapp", + "status": "suspended", + "params": { + "vdcUid": "e3c9e4f1-24da-4992-a003-f8a2a803a5f0", + "nsxtUid": "0fe88e2a-31b6-4385-ad52-e27c6c0d38a6", + "vappName": "vapp-sless" + }, + "out": {}, + "vault_keys": [] + }, + { + "uid": "6214ba32-79e1-435d-9a21-6e41e563daf5", + "name": "vm-sless-demo-vapp", + "status": "suspended", + "params": { + "vdcUid": "e3c9e4f1-24da-4992-a003-f8a2a803a5f0", + "nsxtUid": "0fe88e2a-31b6-4385-ad52-e27c6c0d38a6", + "vappName": "vapp-sless-demo" + }, + "out": {}, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_28_Виртуальная_машина.json b/generated/test/output_inventory/svc_28_Виртуальная_машина.json new file mode 100644 index 0000000..fcc37c6 --- /dev/null +++ b/generated/test/output_inventory/svc_28_Виртуальная_машина.json @@ -0,0 +1,66 @@ +{ + "serviceId": 28, + "svc_code": "Виртуальная машина", + "instances": [ + { + "uid": "a9f8779d-4b8f-40be-bcaf-9427fab6ce4b", + "name": "vm-sless-1", + "status": "suspended", + "params": { + "vmCpu": "2", + "vmRam": "2", + "vmDisk": "20", + "vmName": "web02", + "imageVm": "Ubuntu_22-20G", + "vappUid": "f037ddea-fc1d-46c5-9eef-577e07878c9c", + "cloudInit": "", + "userLogin": "ubuntu", + "ipSpaceName": "internet-ipv4-v1", + "accessIpList": [], + "userPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKQB+Kyevn0H8QSdfm6ZpCU9jtskBxUS9BV0+i1/M04A sless-demo-vm", + "accessPortList": [ + "22" + ] + }, + "out": { + "fqdn": "web02.vapp-sless.a9f8779d-4b8f-40be-bcaf-9427fab6ce4b.dev.nubes.ru", + "monitoring": { + "allDashboards": "https://grafana.ngcloud.ru/d/b14bce94-2f96-4b7f-a424-a59f0df055ad/vm?orgId=954&var-VM_NAME=web02-tUuP-cloud" + }, + "externalConnect": "185.247.187.154", + "internalConnect": "10.10.102.6" + }, + "vault_keys": [] + }, + { + "uid": "212231af-e95a-4f80-b0f6-795d9b23d780", + "name": "vm-sless", + "status": "suspended", + "params": { + "vmCpu": "2", + "vmRam": "2", + "vmDisk": "20", + "vmName": "web01", + "imageVm": "Ubuntu_22-20G", + "vappUid": "fba91c08-3ca4-4c4f-8d0c-f92b75c9e67e", + "cloudInit": "", + "userLogin": "ubuntu", + "ipSpaceName": "internet-ipv4-v1", + "accessIpList": [], + "userPublicKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKQB+Kyevn0H8QSdfm6ZpCU9jtskBxUS9BV0+i1/M04A sless-demo-vm", + "accessPortList": [ + "22" + ] + }, + "out": { + "fqdn": "web01.vapp-sless.212231af-e95a-4f80-b0f6-795d9b23d780.dev.nubes.ru", + "monitoring": { + "allDashboards": "https://grafana.ngcloud.ru/d/b14bce94-2f96-4b7f-a424-a59f0df055ad/vm?orgId=954&var-VM_NAME=web01-POhS-cloud" + }, + "externalConnect": "185.247.187.154", + "internalConnect": "10.10.102.6" + }, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_81_Apache_Superset.json b/generated/test/output_inventory/svc_81_Apache_Superset.json new file mode 100644 index 0000000..00d13d3 --- /dev/null +++ b/generated/test/output_inventory/svc_81_Apache_Superset.json @@ -0,0 +1,26 @@ +{ + "serviceId": 81, + "svc_code": "Apache Superset", + "instances": [ + { + "uid": "A6998702-90EA-4006-B19D-4EBBAD9EBBE8", + "name": "inv-svc-81-1774957205", + "serviceId": 81, + "svc_code": "Apache Superset", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "546FABC3-4113-4B10-8A60-F5CCAC36B34A", + "name": "inv-svc-81-r4", + "serviceId": 81, + "svc_code": "Apache Superset", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_82_Container Registry.json b/generated/test/output_inventory/svc_82_Container Registry.json new file mode 100644 index 0000000..2e02b6f --- /dev/null +++ b/generated/test/output_inventory/svc_82_Container Registry.json @@ -0,0 +1,43 @@ +{ + "serviceId": 82, + "svc_code": "Container Registry", + "instances": [ + { + "uid": "55c4914e-7bc5-4bcc-bc71-2444025bc6fc", + "name": "pearlharbor", + "serviceId": 82, + "svc_code": "Container Registry", + "status": "running", + "source": "prod", + "params": { + "s3Uid": "6d6061cb-b0c1-44b9-8969-a70f08fe673c", + "domain": "pearlharbor", + "emails": "tazet@narod.ru", + "resourceCPU": 100, + "resourceRealm": "k8s-3.ext.nubes.ru", + "resourceMemory": 200, + "resourceInstances": 1 + }, + "out": { + "monitoring": { + "base": { + "url": "https://grafana.ngcloud.ru/d/55c4914e-7bc5-4bcc-bc71-2444025bc6fc-def?orgId=28" + }, + "loki": { + "url": "https://grafana.ngcloud.ru/d/55c4914e-7bc5-4bcc-bc71-2444025bc6fc-log?orgId=28" + }, + "k8sregistry": { + "url": "https://grafana.ngcloud.ru/d/55c4914e-7bc5-4bcc-bc71-2444025bc6fc-krg?orgId=28" + }, + "allDashboards": "https://grafana.ngcloud.ru/dashboards/f/55c4914e-7bc5-4bcc-bc71-2444025bc6fc?orgId=28" + }, + "connectionUrl": "https://pearlharbor.registryk8s.services.ngcloud.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_89_Web-сервер_с_фреймворком_Flask.json b/generated/test/output_inventory/svc_89_Web-сервер_с_фреймворком_Flask.json new file mode 100644 index 0000000..33a01c1 --- /dev/null +++ b/generated/test/output_inventory/svc_89_Web-сервер_с_фреймворком_Flask.json @@ -0,0 +1,64 @@ +{ + "serviceId": 89, + "svc_code": "Web-сервер с фреймворком Flask", + "instances": [ + { + "uid": "7E6A86A6-5F49-4A5E-BD58-DB213DBB2EA0", + "name": "inv-svc-89-1774956904", + "serviceId": 89, + "svc_code": "Web-сервер с фреймворком Flask", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "704F63C7-5F40-40A6-B274-853F6139768D", + "name": "inv-svc-89-r2", + "serviceId": 89, + "svc_code": "Web-сервер с фреймворком Flask", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "1883104F-6C0B-4491-AE4C-E7D3F621104A", + "name": "inv-svc-89-r3", + "serviceId": 89, + "svc_code": "Web-сервер с фреймворком Flask", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "63DEB1C6-52D5-4EE8-BC48-C8B66AB30AAD", + "name": "inv-flask-r4", + "serviceId": 89, + "svc_code": "Web-сервер с фреймворком Flask", + "status": "running", + "params": { + "domain": "web01", + "gitPath": "https://github.com/Foxyhhd/Baldurs-Gate-test.git", + "jsonEnv": {}, + "healthPath": "", + "resourceCPU": 300, + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceMemory": 256, + "resourceInstances": 1 + }, + "out": { + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=63deb1c6-52d5-4ee8-bc48-c8b66ab30aad&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "connectionUrl": "https://web01.pythonk8s.dev.nubes.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_90_PostgreSQL.json b/generated/test/output_inventory/svc_90_PostgreSQL.json new file mode 100644 index 0000000..689d529 --- /dev/null +++ b/generated/test/output_inventory/svc_90_PostgreSQL.json @@ -0,0 +1,211 @@ +{ + "serviceId": 90, + "svc_code": "PostgreSQL", + "instances": [ + { + "uid": "bba55e0f-4c51-4312-8ad3-efc557de331d", + "name": "pg-sless-demo", + "status": "suspended", + "params": { + "s3Uid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "autoScale": "false", + "allowNoSSL": "false", + "appVersion": "17", + "resourceCPU": 500, + "resourceDisk": "1", + "resourceRealm": "naeel-test-3", + "jsonParameters": { + "log_connections": "off", + "log_disconnections": "off" + }, + "resourceMemory": 512, + "autoScaleQuotaGb": "1", + "ipSpaceNameSlave": "", + "ipSpaceNameMaster": "", + "resourceInstances": 1, + "autoScalePercentage": "10", + "autoScaleTechWindow": "0", + "enablePgPoolerSlave": "false", + "ext_BACKUP_SCHEDULE": "0 0 * * *", + "enablePgPoolerMaster": "false", + "ext_BACKUP_NUM_TO_RETAIN": "7", + "needExternalAddressSlave": "false", + "needExternalAddressMaster": "false" + }, + "out": { + "users": { + "user0": { + "role": "ddl_user", + "username": "user0" + } + }, + "databases": { + "db0": { + "dbName": "db0", + "dbOwner": "user0" + } + }, + "monitoring": { + "base": { + "url": "https://grafana.ngcloud.ru/d/bba55e0f-4c51-4312-8ad3-efc557de331d-def?orgId=962" + }, + "loki": { + "url": "https://grafana.ngcloud.ru/d/bba55e0f-4c51-4312-8ad3-efc557de331d-log?orgId=962" + }, + "k8spostgresql": { + "url": "https://grafana.ngcloud.ru/d/bba55e0f-4c51-4312-8ad3-efc557de331d-kpg?orgId=962" + }, + "resourceMetrics": "https://grafana.ngcloud.ru/dashboards/f/bba55e0f-4c51-4312-8ad3-efc557de331d?orgId=962", + "k8spostgresqlbackup": { + "url": "https://grafana.ngcloud.ru/d/bba55e0f-4c51-4312-8ad3-efc557de331d-bpg?orgId=962" + } + }, + "externalConnect": { + "slave": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + }, + "master": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + } + }, + "internalConnect": { + "slave": "", + "master": "postgresqlk8s-master.bba55e0f-4c51-4312-8ad3-efc557de331d.svc.cluster.local" + } + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + }, + { + "uid": "f87f606f-b32e-4eb2-9563-8c3bf88a2d82", + "name": "pg4tf033", + "status": "suspended", + "params": { + "s3Uid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "autoScale": "false", + "allowNoSSL": "false", + "appVersion": "17", + "resourceCPU": 500, + "resourceDisk": "1", + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "jsonParameters": { + "log_connections": "off", + "log_disconnections": "off" + }, + "resourceMemory": 512, + "autoScaleQuotaGb": "1", + "ipSpaceNameSlave": "", + "ipSpaceNameMaster": "", + "resourceInstances": 1, + "autoScalePercentage": "10", + "autoScaleTechWindow": "0", + "enablePgPoolerSlave": "false", + "ext_BACKUP_SCHEDULE": "0 0 * * *", + "enablePgPoolerMaster": "false", + "ext_BACKUP_NUM_TO_RETAIN": "7", + "needExternalAddressSlave": "false", + "needExternalAddressMaster": "false" + }, + "out": { + "users": {}, + "databases": {}, + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=f87f606f-b32e-4eb2-9563-8c3bf88a2d82&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "externalConnect": { + "slave": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + }, + "master": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + } + }, + "internalConnect": { + "slave": "", + "master": "postgresqlk8s-master.f87f606f-b32e-4eb2-9563-8c3bf88a2d82.svc.cluster.local" + } + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + }, + { + "uid": "f97978e3-9300-41bb-b22c-4f9aecf674f8", + "name": "pg4tf011", + "status": "suspended", + "params": { + "s3Uid": "332cdb0d-34bf-43bf-864d-4adcc3b556fb", + "autoScale": "false", + "allowNoSSL": "false", + "appVersion": "17", + "resourceCPU": 500, + "resourceDisk": "1", + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "jsonParameters": { + "log_connections": "off", + "log_disconnections": "off" + }, + "resourceMemory": 512, + "autoScaleQuotaGb": "1", + "ipSpaceNameSlave": "", + "ipSpaceNameMaster": "", + "resourceInstances": 1, + "autoScalePercentage": "10", + "autoScaleTechWindow": "0", + "enablePgPoolerSlave": "false", + "ext_BACKUP_SCHEDULE": "0 0 * * *", + "enablePgPoolerMaster": "false", + "ext_BACKUP_NUM_TO_RETAIN": "7", + "needExternalAddressSlave": "false", + "needExternalAddressMaster": "false" + }, + "out": { + "users": {}, + "databases": {}, + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=f97978e3-9300-41bb-b22c-4f9aecf674f8&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "externalConnect": { + "slave": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + }, + "master": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + } + }, + "internalConnect": { + "slave": "", + "master": "postgresqlk8s-master.f97978e3-9300-41bb-b22c-4f9aecf674f8.svc.cluster.local" + } + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_91_Redis.json b/generated/test/output_inventory/svc_91_Redis.json new file mode 100644 index 0000000..132817a --- /dev/null +++ b/generated/test/output_inventory/svc_91_Redis.json @@ -0,0 +1,70 @@ +{ + "serviceId": 91, + "svc_code": "Redis", + "instances": [ + { + "uid": "A47F8E86-7684-44A3-8319-C6801C671FC0", + "name": "inv-redis-1774955749", + "serviceId": 91, + "svc_code": "Redis", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "1A8EB2E1-30A1-41A0-A488-B03CDFBDAE63", + "name": "inv-redis-1774955832", + "serviceId": 91, + "svc_code": "Redis", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "76E0B60D-9F29-4C27-9EA3-9423608D1A98", + "name": "inv-redis-test", + "serviceId": 91, + "svc_code": "Redis", + "status": "running", + "params": { + "resourceCPU": 1000, + "resourceDisk": "30", + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceMemory": 1024, + "resourceInstances": 1, + "needExternalAddressSlave": "false", + "needExternalAddressMaster": "false" + }, + "out": { + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=76e0b60d-9f29-4c27-9ea3-9423608d1a98&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "externalConnect": { + "slave": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + }, + "master": { + "ip": "", + "fqdn": "", + "uuid": "", + "isExternal": false + } + }, + "internalConnect": { + "slave": "", + "master": "redisk8s.76e0b60d-9f29-4c27-9ea3-9423608d1a98.svc.k8s-3-sandbox-nubes-ru" + } + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_92_MongoDB.json b/generated/test/output_inventory/svc_92_MongoDB.json new file mode 100644 index 0000000..1a23746 --- /dev/null +++ b/generated/test/output_inventory/svc_92_MongoDB.json @@ -0,0 +1,44 @@ +{ + "serviceId": 92, + "svc_code": "MongoDB", + "instances": [ + { + "uid": "2DE9569A-0C17-40FD-A591-027F18658E3F", + "name": "inv-svc-92-1774956293", + "serviceId": 92, + "svc_code": "MongoDB", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "01CD4576-9DC1-42C7-8F32-B1C75CA234D8", + "name": "inv-svc-92-r3", + "serviceId": 92, + "svc_code": "MongoDB", + "status": "running", + "params": { + "resourceCPU": 1000, + "resourceDisk": "10", + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceMemory": 1024, + "resourceInstances": 1, + "needExternalAddressMaster": "false" + }, + "out": { + "monitoring": { + "base": { + "url": "https://grafana.ngcloud.ru/d/01cd4576-9dc1-42c7-8f32-b1c75ca234d8-def?orgId=962" + }, + "loki": { + "url": "https://grafana.ngcloud.ru/d/01cd4576-9dc1-42c7-8f32-b1c75ca234d8-log?orgId=962" + }, + "allDashboards": "https://grafana.ngcloud.ru/dashboards/f/01cd4576-9dc1-42c7-8f32-b1c75ca234d8?orgId=962" + }, + "internalConnect": "mongok8s-psmdb-db-rs0.01cd4576-9dc1-42c7-8f32-b1c75ca234d8.svc.k8s-3-sandbox-nubes-ru" + }, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_93_RabbitMQ.json b/generated/test/output_inventory/svc_93_RabbitMQ.json new file mode 100644 index 0000000..bd5329b --- /dev/null +++ b/generated/test/output_inventory/svc_93_RabbitMQ.json @@ -0,0 +1,48 @@ +{ + "serviceId": 93, + "svc_code": "RabbitMQ", + "instances": [ + { + "uid": "1dbfe9da-ce1c-4958-b359-d016a4b455c8", + "name": "sless-rabbit", + "status": "running", + "params": { + "resourceCPU": 500, + "resourceDisk": "5", + "resourceRealm": "naeel-test-3", + "resourceMemory": 512, + "resourceInstances": 1, + "needExternalAddressMaster": "false" + }, + "out": { + "monitoring": { + "base": { + "url": "https://grafana.ngcloud.ru/d/1dbfe9da-ce1c-4958-b359-d016a4b455c8-def?orgId=962" + }, + "loki": { + "url": "https://grafana.ngcloud.ru/d/1dbfe9da-ce1c-4958-b359-d016a4b455c8-log?orgId=962" + }, + "resourceMetrics": "https://grafana.ngcloud.ru/dashboards/f/1dbfe9da-ce1c-4958-b359-d016a4b455c8?orgId=962" + }, + "clusterDomain": "cluster.local", + "inernalConnect": { + "slave": "", + "master": "rabbitmqk8s.1dbfe9da-ce1c-4958-b359-d016a4b455c8.svc.cluster.local" + }, + "externalConnect": { + "admin": { + "ip": "185.247.187.147", + "fqdn": "admin.1dbfe9da-ce1c-4958-b359-d016a4b455c8.dev.nubes.ru", + "uuid": "9fa09e8c-2725-4efa-ace6-83123ed5ebfc", + "isExternal": false + } + } + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_94_Lucee.json b/generated/test/output_inventory/svc_94_Lucee.json new file mode 100644 index 0000000..b38925e --- /dev/null +++ b/generated/test/output_inventory/svc_94_Lucee.json @@ -0,0 +1,43 @@ +{ + "serviceId": 94, + "svc_code": "Lucee", + "instances": [ + { + "uid": "86d5253c-b0fd-435c-9951-4a7d2e2f8e04", + "name": "lucy_teststand_0", + "status": "suspended", + "params": { + "domain": "web-test-stand", + "gitPath": "https://gitea-naeel.giteak8s.services.ngcloud.ru/naeel/testlucee", + "jsonEnv": { + "testds_class": "org.postgresql.Driver", + "testds_password": "zCDy6Wt6qBDXO3UlQl5ZgooxYElvY5LdBvTuiMZWLaKtDIF15uWoVM51YEZWTZHI", + "testds_username": "user0", + "testds_validate": "false", + "testds_bundleName": "org.postgresql.jdbc", + "testds_liveTimeout": "15", + "testds_bundleVersion": "42.6.0", + "testds_connectionLimit": "5", + "testds_connectionString": "jdbc:postgresql://postgresqlk8s-master.f97978e3-9300-41bb-b22c-4f9aecf674f8.svc.cluster.local:5432/postgres?sslmode=require" + }, + "appVersion": "5.4", + "healthPath": "", + "resourceCPU": 300, + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceMemory": 512, + "resourceInstances": 1 + }, + "out": { + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=86d5253c-b0fd-435c-9951-4a7d2e2f8e04&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "connectionUrl": "https://web-test-stand.luceek8s.dev.nubes.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_95_NodeJS.json b/generated/test/output_inventory/svc_95_NodeJS.json new file mode 100644 index 0000000..d94bf25 --- /dev/null +++ b/generated/test/output_inventory/svc_95_NodeJS.json @@ -0,0 +1,41 @@ +{ + "serviceId": 95, + "svc_code": "NodeJS", + "instances": [ + { + "uid": "6bfdb1d0-5d32-437e-9086-b2544b39e2cd", + "name": "node_01", + "status": "suspended", + "params": { + "domain": "node07", + "gitPath": "https://gitea-naeel.giteak8s.services.ngcloud.ru/naeel/testnode.git", + "jsonEnv": { + "PGHOST": "postgresqlk8s-master.f97978e3-9300-41bb-b22c-4f9aecf674f8.svc.cluster.local", + "PGPORT": "5432", + "PGUSER": "user0", + "PGSSLMODE": "require", + "PGDATABASE": "db_terra", + "PGPASSWORD": "zCDy6Wt6qBDXO3UlQl5ZgooxYElvY5LdBvTuiMZWLaKtDIF15uWoVM51YEZWTZHI", + "DATABASE_URL": "postgresql://user0:zCDy6Wt6qBDXO3UlQl5ZgooxYElvY5LdBvTuiMZWLaKtDIF15uWoVM51YEZWTZHI@postgresqlk8s-master.f97978e3-9300-41bb-b22c-4f9aecf674f8.svc.cluster.local:5432/db_terra?sslmode=require" + }, + "appVersion": "23", + "healthPath": "/healthz", + "resourceCPU": 300, + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceMemory": 256, + "resourceInstances": 1 + }, + "out": { + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=6bfdb1d0-5d32-437e-9086-b2544b39e2cd&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "connectionUrl": "https://node07.nodejsk8s.dev.nubes.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_96_pgAdmin.json b/generated/test/output_inventory/svc_96_pgAdmin.json new file mode 100644 index 0000000..0fdd4a3 --- /dev/null +++ b/generated/test/output_inventory/svc_96_pgAdmin.json @@ -0,0 +1,54 @@ +{ + "serviceId": 96, + "svc_code": "pgAdmin", + "instances": [ + { + "uid": "6A28DD42-F374-4EAF-90FE-03833612BA28", + "name": "inv-svc-96-1774957391", + "serviceId": 96, + "svc_code": "pgAdmin", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "E5FD046C-553B-4EFD-9E8B-6A0DFD955EDB", + "name": "inv-svc-96-r2", + "serviceId": 96, + "svc_code": "pgAdmin", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "F2D61F0E-5E7E-4D9D-BD57-DC2A3F793CF5", + "name": "inv-svc-96-r3", + "serviceId": 96, + "svc_code": "pgAdmin", + "status": "running", + "params": { + "login": "admin@ngcloud.ru", + "domain": "web01", + "password": "********", + "resourceCPU": 500, + "resourceDisk": "1", + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceMemory": 512, + "resourceInstances": 1 + }, + "out": { + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=f2d61f0e-5e7e-4d9d-bd57-dc2a3f793cf5&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "connectionUrl": "https://web01.pgadmink8s.dev.nubes.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_97_NodeRed.json b/generated/test/output_inventory/svc_97_NodeRed.json new file mode 100644 index 0000000..57fe9d6 --- /dev/null +++ b/generated/test/output_inventory/svc_97_NodeRed.json @@ -0,0 +1,50 @@ +{ + "serviceId": 97, + "svc_code": "NodeRed", + "instances": [ + { + "uid": "FD777742-39B0-4C8D-8F7A-263A55C63D24", + "name": "inv-svc-97-1774956994", + "serviceId": 97, + "svc_code": "NodeRed", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "E92ECCAC-D643-4F0C-BB52-16078B1678F4", + "name": "inv-svc-97-r2", + "serviceId": 97, + "svc_code": "NodeRed", + "status": "running", + "params": { + "resourceRealm": "k8s-3-sandbox-nubes-ru" + }, + "out": {}, + "vault_keys": [] + }, + { + "uid": "0146BF85-164C-4DEA-8591-6056FECF479A", + "name": "inv-svc-97-r4", + "serviceId": 97, + "svc_code": "NodeRed", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "09D788A5-4D8D-4133-8F27-C36B6C8113EC", + "name": "inv-svc-97-r5", + "serviceId": 97, + "svc_code": "NodeRed", + "status": "running", + "params": { + "resourceRealm": "k8s-3-sandbox-nubes-ru" + }, + "out": {}, + "vault_keys": [] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_98_Простой_HTTP_контейнер.json b/generated/test/output_inventory/svc_98_Простой_HTTP_контейнер.json new file mode 100644 index 0000000..22b1462 --- /dev/null +++ b/generated/test/output_inventory/svc_98_Простой_HTTP_контейнер.json @@ -0,0 +1,45 @@ +{ + "serviceId": 98, + "svc_code": "Простой HTTP контейнер", + "instances": [ + { + "uid": "AF8C0CB4-AF45-4D00-A39C-1A53D8A1C843", + "name": "inv-svc-98-1774956936", + "serviceId": 98, + "svc_code": "Простой HTTP контейнер", + "status": "not created", + "params": {}, + "out": {}, + "vault_keys": [] + }, + { + "uid": "9BD1957F-BBBA-4767-970A-AA4FE4D818D6", + "name": "inv-svc-98-r2", + "serviceId": 98, + "svc_code": "Простой HTTP контейнер", + "status": "running", + "params": { + "domain": "web01", + "jsonEnv": { + "param": "value" + }, + "resourceCPU": 100, + "registryPath": "nexus-sa.tst.nubes.ru/docker-nubes/jolt", + "resourceRealm": "k8s-3-sandbox-nubes-ru", + "resourceMemory": 200, + "resourceInstances": 1 + }, + "out": { + "monitoring": { + "resourceMetrics": "https://grafana.ngcloud.ru/d/vzjb4zd/kubernetes-pods?orgId=954&var-namespace=9bd1957f-bbba-4767-970a-aa4fe4d818d6&var-resource_realm=k8s-3-sandbox-nubes-ru" + }, + "connectionUrl": "https://web01.containerk8s.dev.nubes.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/output_inventory/svc_99_Gitea.json b/generated/test/output_inventory/svc_99_Gitea.json new file mode 100644 index 0000000..dd8baa5 --- /dev/null +++ b/generated/test/output_inventory/svc_99_Gitea.json @@ -0,0 +1,40 @@ +{ + "serviceId": 99, + "svc_code": "Gitea", + "instances": [ + { + "uid": "e15e66e1-c613-4230-8f5e-263804224b0f", + "name": "naeel_git", + "serviceId": 99, + "svc_code": "Gitea", + "status": "running", + "source": "prod", + "params": { + "domain": "gitea-naeel", + "psqlUid": "b1d8ab38-bd8a-4be4-a27c-9b1a74dc0f19", + "resourceCPU": 500, + "resourceDisk": "1", + "resourceRealm": "k8s-3.ext.nubes.ru", + "resourceMemory": 512, + "resourceInstances": 1 + }, + "out": { + "monitoring": { + "base": { + "url": "https://grafana.ngcloud.ru/d/e15e66e1-c613-4230-8f5e-263804224b0f-def?orgId=28" + }, + "loki": { + "url": "https://grafana.ngcloud.ru/d/e15e66e1-c613-4230-8f5e-263804224b0f-log?orgId=28" + }, + "allDashboards": "https://grafana.ngcloud.ru/dashboards/f/e15e66e1-c613-4230-8f5e-263804224b0f?orgId=28" + }, + "connectionUrl": "https://gitea-naeel.giteak8s.services.ngcloud.ru" + }, + "vault_keys": [ + "url", + "fields", + "userPath" + ] + } + ] +} \ No newline at end of file diff --git a/generated/test/resources_yaml/.gitkeep b/generated/test/resources_yaml/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/provider/resources_yaml/100_openwhisk.yaml b/generated/test/resources_yaml/100_openwhisk.yaml similarity index 100% rename from provider/resources_yaml/100_openwhisk.yaml rename to generated/test/resources_yaml/100_openwhisk.yaml diff --git a/generated/test/resources_yaml/110_dnszone.yaml b/generated/test/resources_yaml/110_dnszone.yaml new file mode 100644 index 0000000..03d2540 --- /dev/null +++ b/generated/test/resources_yaml/110_dnszone.yaml @@ -0,0 +1,94 @@ +name: dnszone +service_id: 110 +service_display_name: DNS зона +service_short_name: dnszone +service_man: '## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
```' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 43 + kind: instance + action: create + params: + - id: 66 + code: zoneEmail + data_type: string + required: true + descr: Контактный e-mail + - id: 67 + code: zoneName + data_type: string + required: true + regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ + descr: Имя зоны + man: Имя зоны + - id: 68 + code: zoneKind + data_type: string + required: true + default: NATIVE + value_list: + - NATIVE + - SLAVE + descr: Тип зоны + - id: 69 + code: zoneMastersForSlave + data_type: string + required: false + descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов + - id: 70 + code: zoneTimeToRefresh + data_type: string + required: true + default: "86400" + descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов + - id: 71 + code: zoneTimeToRetry + data_type: string + required: true + default: "7200" + descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов + - id: 72 + code: zoneTimeToExpire + data_type: string + required: true + default: "3600000" + descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным + - id: 73 + code: zoneTimeToLive + data_type: string + required: true + default: "86400" + descr: Время, в течение которого информация будет кешироваться другими DNS-серверами + - id: 284 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + - name: delete + id: 44 + kind: instance + action: delete + params: [] diff --git a/provider/resources_yaml/111_dnsrecord.yaml b/generated/test/resources_yaml/111_dnsrecord.yaml similarity index 100% rename from provider/resources_yaml/111_dnsrecord.yaml rename to generated/test/resources_yaml/111_dnsrecord.yaml diff --git a/generated/test/resources_yaml/112_tenant.yaml b/generated/test/resources_yaml/112_tenant.yaml new file mode 100644 index 0000000..a7ae715 --- /dev/null +++ b/generated/test/resources_yaml/112_tenant.yaml @@ -0,0 +1,57 @@ +name: tenant +service_id: 112 +service_display_name: Тенант в Grafana +service_short_name: tenant +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 84 + kind: instance + action: create + params: + - id: 239 + code: grafanaOrgName + data_type: string + required: true + regex: ^WZ[\d]+$ + descr: WZ-Код клиента + sort: 10 + - id: 240 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 20 + - name: delete + id: 86 + kind: instance + action: delete + params: [] + - name: modify + id: 87 + kind: instance + action: modify + man: Просто удаляет все текущие датасоурсы и ставит их заново
Полезно, когда нужно "ресетнуть" / "добавить" + params: [] diff --git a/generated/test/resources_yaml/113_vc_complex.yaml b/generated/test/resources_yaml/113_vc_complex.yaml new file mode 100644 index 0000000..038f922 --- /dev/null +++ b/generated/test/resources_yaml/113_vc_complex.yaml @@ -0,0 +1,155 @@ +name: vc_complex +service_id: 113 +service_display_name: Быстрый старт +service_short_name: vcComplex +service_man: '# Создает окружение для ВМ.

## Создает сервисы:
**Организация**
**Виртуальный датацентр**
**Каталог**
**Сетевой шлюз периметра**

' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 107 + kind: instance + action: create + params: + - id: 332 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование ресурсной платформы + sort: 1 + - id: 334 + code: vappName + data_type: string + required: true + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + descr: Имя vApp + sort: 10 + - id: 358 + code: vdcProviderGateway + data_type: string + required: true + default: sandbox-v1cl1-pvdc + descr: Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String + sort: 200 + - id: 359 + code: virtualServicesCount + required: true + default: "1" + maxvalue: 4 + minvalue: 1 + descr: Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 + sort: 110 + - id: 360 + code: ipSpaceName + data_type: string + required: false + default: internet-ipv4-v1 + descr: Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT + sort: 130 + - id: 399 + code: storageConfig + data_type: json + required: true + default: '[{"name": "", "size": 1024}]' + descr: Описывается как массив объектов, где описывается имя политики и её размер + sort: 210 + - id: 400 + code: vdcNetworkPool + data_type: string + required: true + default: nsxt-sandbox-geneve-np + sort: 120 + - id: 401 + code: cpuGuaranteed + data_type: integer > 0 + required: true + default: "20" + value_list: + - "20" + - "50" + - "80" + sort: 20 + - id: 402 + code: memGuaranteed + data_type: integer > 0 + required: true + default: "20" + value_list: + - "20" + - "50" + - "80" + sort: 30 + - id: 403 + code: segroupName + data_type: string + required: false + default: SEGROUP-SANDBOX-CL1-SHARED-01 + descr: Наименование segroup, в котором будут выделяться пулы на AVI + sort: 100 + - id: 404 + code: needExternalAddressSNAT + data_type: boolean + required: true + default: "true" + value_list: + - "false" + - "true" + descr: Выделяет внешний IP на Edge. Используется для SNAT всего трафика + sort: 40 + - id: 405 + code: needEnableAVI + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Включает AVI в Cloud Director + sort: 50 + - id: 566 + code: cpuAllocated + data_type: string + required: true + default: "1" + minvalue: 1 + descr: шт + sort: 21 + - id: 567 + code: memAllocated + data_type: integer > 0 + required: true + default: "10" + minvalue: 1 + descr: gb + sort: 31 + - id: 568 + code: vIPCount + data_type: integer > 0 + required: true + default: "0" + maxvalue: 20 + minvalue: 0 + descr: ш + sort: 131 diff --git a/generated/test/resources_yaml/114_gitea_complex.yaml b/generated/test/resources_yaml/114_gitea_complex.yaml new file mode 100644 index 0000000..b18075e --- /dev/null +++ b/generated/test/resources_yaml/114_gitea_complex.yaml @@ -0,0 +1,28 @@ +name: gitea_complex +service_id: 114 +service_display_name: Комплексная услуга по созданию gitea +service_short_name: GiteaComplex +service_man: Комплексная услуга из ["gittea", "postgres"] +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: [] diff --git a/provider/resources_yaml/115_mariadb.yaml b/generated/test/resources_yaml/115_mariadb.yaml similarity index 100% rename from provider/resources_yaml/115_mariadb.yaml rename to generated/test/resources_yaml/115_mariadb.yaml diff --git a/generated/test/resources_yaml/116_kafka.yaml b/generated/test/resources_yaml/116_kafka.yaml new file mode 100644 index 0000000..b0b9c6d --- /dev/null +++ b/generated/test/resources_yaml/116_kafka.yaml @@ -0,0 +1,291 @@ +name: kafka +service_id: 116 +service_display_name: ApacheKafka +service_short_name: kafka +service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Идентификатор организации
Уникальный идентификатор созданной организации. Используется для всех дальнейших API-операций.

### Статус организации
Отражает текущее состояние: active, suspended, modifying, deleting.

### Параметры сети
Выходные данные при наличии ipSpaces: список выделенных внешних IP, имя ip_space, количество IP.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)**;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к этой организации.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.

' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 148 + kind: instance + action: create + params: + - id: 461 + code: resourceInstances + data_type: integer > 0 + required: true + value_list: + - "1" + - "2" + - "3" + - "4" + - "5" + minvalue: 1 + descr: 'Пример: `2`' + sort: 20 + is_modifiable: true + - id: 462 + code: resourceMemory + data_type: integer > 0 + required: true + minvalue: 1000 + descr: 'Пример: `2048`' + sort: 30 + is_modifiable: true + - id: 463 + code: resourceCPU + data_type: integer > 0 + required: true + minvalue: 1000 + descr: 'Пример: `2000`' + sort: 40 + is_modifiable: true + - id: 464 + code: resourceDisk + data_type: integer > 0 + required: true + descr: 'Пример: `20`' + sort: 50 + is_modifiable: true + - id: 465 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`' + sort: 60 + is_modifiable: true + - id: 466 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' + sort: 70 + is_modifiable: true + - id: 469 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 5 + - name: create_topic + id: 165 + kind: subresource + action: create + subresource: topic + man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' + params: + - id: 523 + code: nameTopic + data_type: string + required: true + descr: 'Пример: `orders-events`' + sort: 10 + - id: 524 + code: partitions + data_type: integer > 0 + required: true + descr: Число партиций можно увеличить, но уменьшить их нельзя. + - id: 525 + code: replicas + data_type: integer > 0 + required: true + descr: Количество реплик не может привышать количество реплик брокера + sort: 20 + - name: create_user + id: 168 + kind: subresource + action: create + subresource: user + man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' + params: + - id: 530 + code: username + data_type: string + required: true + descr: 'Пример: `username-admin`' + - id: 531 + code: nameTopic + data_type: string + required: true + descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' + - id: 532 + code: operations + data_type: string + required: true + descr: 'Пример: `Create, Describe, Read, Write`' + - id: 533 + code: group + data_type: string + required: true + descr: 'Пример: `order-consumers` или `*`' + - id: 534 + code: accessHosts + data_type: string + required: true + descr: 'Пример: `192.168.1.0/24` или `*`' + - name: delete + id: 149 + kind: instance + action: delete + params: [] + - name: delete_topic + id: 167 + kind: subresource + action: delete + subresource: topic + params: + - id: 529 + code: nameTopic + data_type: string + required: true + descr: Необходимо выбрать топик, который необходимо удалить + - name: delete_user + id: 170 + kind: subresource + action: delete + subresource: user + man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' + params: + - id: 540 + code: username + data_type: string + required: true + descr: 'Пример: `username-admin`' + - name: modify + id: 162 + kind: instance + action: modify + params: + - id: 501 + code: resourceInstances + data_type: integer > 0 + required: false + value_list: + - "1" + - "2" + - "3" + - "4" + - "5" + descr: 'Пример: `2`' + - id: 502 + code: resourceMemory + data_type: integer > 0 + required: false + minvalue: 1000 + descr: 'Пример: `2048`' + - id: 503 + code: resourceCPU + required: false + minvalue: 1000 + descr: 'Пример: `2000`' + - id: 504 + code: resourceDisk + data_type: integer > 0 + required: false + descr: 'Пример: `20`' + - id: 505 + code: needExternalAddressMaster + data_type: boolean + required: false + default: "false" + value_list: + - "false" + - "true" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-брокеру. Пример: `true` или `false`' + - id: 506 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' + - name: modify_topic + id: 166 + kind: subresource + action: modify + subresource: topic + man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' + params: + - id: 526 + code: nameTopic + data_type: string + required: true + descr: 'Пример: `orders-events`' + sort: 10 + - id: 527 + code: partitions + required: false + descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` + sort: 30 + - id: 528 + code: replicas + required: false + descr: Количество партиций ≤ (число брокеров). Пример `2` + sort: 20 + - name: modify_user + id: 169 + kind: subresource + action: modify + subresource: user + man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' + params: + - id: 535 + code: nameTopic + data_type: string + required: false + descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' + - id: 536 + code: operations + data_type: string + required: false + descr: 'Пример: `Create, Describe, Read, Write`' + - id: 537 + code: group + data_type: string + required: false + descr: 'Пример: `order-consumers` или `*`' + - id: 538 + code: accessHosts + data_type: string + required: false + descr: 'Пример: `192.168.1.0/24` или `*`' + - id: 539 + code: username + data_type: string + required: true + descr: 'Пример: `username-admin`' + sort: 10 + - name: resume + id: 161 + kind: instance + action: resume + params: [] + - name: suspend + id: 160 + kind: instance + action: suspend + params: [] diff --git a/generated/test/resources_yaml/117_nifi.yaml b/generated/test/resources_yaml/117_nifi.yaml new file mode 100644 index 0000000..39c49bd --- /dev/null +++ b/generated/test/resources_yaml/117_nifi.yaml @@ -0,0 +1,81 @@ +name: nifi +service_id: 117 +service_display_name: Nifi +service_short_name: nifi +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 150 + kind: instance + action: create + params: + - id: 471 + code: kafkaUid + data_type: uuid + required: true + ref_svc_id: 116 + descr: Выбрать кластер из списка + sort: 10 + - id: 472 + code: partitions + data_type: integer > 0 + required: true + descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` + man: 'Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.' + sort: 30 + is_modifiable: true + - id: 473 + code: replicas + data_type: integer > 0 + required: true + descr: Количество партиций ≤ (число брокеров). Пример `2` + sort: 40 + is_modifiable: true + - id: 474 + code: nameTopic + data_type: string + required: true + descr: 'Пример: `orders-events`' + sort: 20 + - name: delete + id: 151 + kind: instance + action: delete + params: [] + - name: modify + id: 154 + kind: instance + action: modify + params: + - id: 477 + code: partitions + data_type: integer > 0 + required: false + descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` + man: 'Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.' + - id: 478 + code: replicas + data_type: integer > 0 + required: false + descr: Количество партиций ≤ (число брокеров). Пример `2` diff --git a/generated/test/resources_yaml/119_akhq.yaml b/generated/test/resources_yaml/119_akhq.yaml new file mode 100644 index 0000000..7339976 --- /dev/null +++ b/generated/test/resources_yaml/119_akhq.yaml @@ -0,0 +1,104 @@ +name: akhq +service_id: 119 +service_display_name: Akhq +service_short_name: Akhq +service_man: '# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group''ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
---' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 175 + kind: instance + action: create + params: + - id: 550 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 10 + - id: 551 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + minvalue: 512 + descr: 'Пример: `1024`' + sort: 20 + - id: 552 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + minvalue: 500 + descr: 'Пример: `1000`' + sort: 30 + - id: 553 + code: domain + data_type: string + required: true + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 40 + - id: 554 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + minvalue: 1 + sort: 11 + - id: 555 + code: kafkaUid + data_type: uuid + required: true + ref_svc_id: 116 + descr: Выбрать кластер из списка + sort: 5 + is_modifiable: true + - name: delete + id: 176 + kind: instance + action: delete + params: [] + - name: modify + id: 180 + kind: instance + action: modify + params: + - id: 565 + code: kafkaUid + data_type: uuid + required: true + ref_svc_id: 116 + descr: Выбрать кластер из списка + - name: resume + id: 178 + kind: instance + action: resume + params: [] + - name: suspend + id: 177 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/120_clickhouse.yaml b/generated/test/resources_yaml/120_clickhouse.yaml similarity index 100% rename from provider/resources_yaml/120_clickhouse.yaml rename to generated/test/resources_yaml/120_clickhouse.yaml diff --git a/provider/resources_yaml/12_s3.yaml b/generated/test/resources_yaml/12_s3.yaml similarity index 100% rename from provider/resources_yaml/12_s3.yaml rename to generated/test/resources_yaml/12_s3.yaml diff --git a/generated/test/resources_yaml/13_s3bucket.yaml b/generated/test/resources_yaml/13_s3bucket.yaml new file mode 100644 index 0000000..68e506c --- /dev/null +++ b/generated/test/resources_yaml/13_s3bucket.yaml @@ -0,0 +1,90 @@ +name: s3bucket +service_id: 13 +service_display_name: S3 бакет +service_short_name: s3bucket +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 51 + kind: instance + action: create + params: + - id: 124 + code: s3UserUid + data_type: uuid + required: true + ref_svc_id: 12 + descr: UID Корневой услуги S3. На основе него будет создан бакет. + - id: 125 + code: bucketName + data_type: string + required: true + descr: Имя бакета s3 + - id: 126 + code: maxSize + required: true + default: "-1" + descr: Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes + man: Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes + - id: 127 + code: readAll + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. + - id: 128 + code: listAll + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. + - id: 129 + code: corsAll + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS + - id: 130 + code: placement + data_type: string + required: true + default: HOT + value_list: + - HOT + - COLD + descr: Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения + - name: delete + id: 52 + kind: instance + action: delete + params: [] diff --git a/generated/test/resources_yaml/149_valo_tenant.yaml b/generated/test/resources_yaml/149_valo_tenant.yaml new file mode 100644 index 0000000..ddb0afb --- /dev/null +++ b/generated/test/resources_yaml/149_valo_tenant.yaml @@ -0,0 +1,98 @@ +name: valo_tenant +service_id: 149 +service_display_name: VALO Cloud +service_short_name: valoTenant +service_man: '# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 202 + kind: instance + action: create + params: + - id: 655 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + sort: 10 + - id: 656 + code: domain + data_type: string + required: true + regex: ^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$ + maxlength: 61 + minlength: 3 + descr: Будет добавлено в зону valo.ngcloud.ru + sort: 20 + - id: 657 + code: valoOptions + data_type: map-fixed + required: true + default: '{}' + sort: 40 + is_modifiable: true + - id: 658 + code: s3Storage + data_type: integer > 0 + required: true + default: "10" + maxvalue: 999 + minvalue: 1 + descr: Параметр указывается в `Gb` + man: Данные хранятся в отдельном экземпляре S3 + sort: 30 + is_modifiable: true + - name: delete + id: 206 + kind: instance + action: delete + params: [] + - name: modify + id: 203 + kind: instance + action: modify + params: + - id: 659 + code: valoOptions + data_type: map-fixed + required: true + sort: 10 + - id: 660 + code: s3Storage + data_type: integer > 0 + required: true + descr: Параметр указывается в `Gb` + man: Данные хранятся в отдельном экземпляре S3 + sort: 20 + - name: resume + id: 205 + kind: instance + action: resume + params: [] + - name: suspend + id: 204 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/150_k8s_sthutrval_cluster.yaml b/generated/test/resources_yaml/150_k8s_sthutrval_cluster.yaml similarity index 100% rename from provider/resources_yaml/150_k8s_sthutrval_cluster.yaml rename to generated/test/resources_yaml/150_k8s_sthutrval_cluster.yaml diff --git a/provider/resources_yaml/19_vc_org.yaml b/generated/test/resources_yaml/19_vc_org.yaml similarity index 100% rename from provider/resources_yaml/19_vc_org.yaml rename to generated/test/resources_yaml/19_vc_org.yaml diff --git a/provider/resources_yaml/1_dummy.yaml b/generated/test/resources_yaml/1_dummy.yaml similarity index 100% rename from provider/resources_yaml/1_dummy.yaml rename to generated/test/resources_yaml/1_dummy.yaml diff --git a/generated/test/resources_yaml/20_vc_org_saas.yaml b/generated/test/resources_yaml/20_vc_org_saas.yaml new file mode 100644 index 0000000..1520bdf --- /dev/null +++ b/generated/test/resources_yaml/20_vc_org_saas.yaml @@ -0,0 +1,57 @@ +name: vc_org_saas +service_id: 20 +service_display_name: Организация [DEPRECATED] +service_short_name: vcOrgSaas +service_man: 'Верхнеуровневая сущность создаваемая для Заказчика.
Внутри Организация вы можете создать: Виртуальные датацентры, Каталоги.' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 8 + kind: instance + action: create + man: Создание организации в Cloud Director
Id в клауд директоре создаётся на основе contragentCode + params: + - id: 185 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование ресурсной платформы + sort: 30 + - name: delete + id: 23 + kind: instance + action: delete + params: [] + - name: resume + id: 119 + kind: instance + action: resume + params: [] + - name: suspend + id: 118 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/21_vc_vdc.yaml b/generated/test/resources_yaml/21_vc_vdc.yaml similarity index 100% rename from provider/resources_yaml/21_vc_vdc.yaml rename to generated/test/resources_yaml/21_vc_vdc.yaml diff --git a/provider/resources_yaml/22_vc_nsxt.yaml b/generated/test/resources_yaml/22_vc_nsxt.yaml similarity index 100% rename from provider/resources_yaml/22_vc_nsxt.yaml rename to generated/test/resources_yaml/22_vc_nsxt.yaml diff --git a/generated/test/resources_yaml/23_vc_vm.yaml b/generated/test/resources_yaml/23_vc_vm.yaml new file mode 100644 index 0000000..470873d --- /dev/null +++ b/generated/test/resources_yaml/23_vc_vm.yaml @@ -0,0 +1,153 @@ +name: vc_vm +service_id: 23 +service_display_name: VM в Cloud Director (старый формат) (vc_vm) +service_short_name: vc_vm +service_man: Виртуальная машина без каких-либо особенностей.
Данная услуга запускает vc_ip для выдачи белого адреса и подключения к ВМ +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 26 + kind: instance + action: create + params: + - id: 33 + code: vappUid + data_type: uuid + required: true + ref_svc_id: 26 + descr: InstanceUid vApp. vApp должен быть создан + man: InstanceUid vApp. vApp должен быть создан + sort: 10 + - id: 35 + code: vmName + data_type: string + required: true + default: vm0001 + regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ + descr: Имя ВМ. Не поддерживает спец-символы + man: Имя ВМ. Не поддерживает спец-символы + sort: 20 + - id: 36 + code: vmCpu + data_type: string + required: true + default: "2" + descr: CPU + man: CPU + sort: 30 + is_modifiable: true + - id: 37 + code: vmRam + data_type: string + required: true + default: "2" + descr: RAM (GB) + man: RAM (GB) + sort: 40 + is_modifiable: true + - id: 38 + code: vmDisksize + data_type: string + required: true + default: "10" + descr: Размер диска под сервис (GB) + man: Размер диска под сервис (GB) + sort: 50 + is_modifiable: true + - id: 176 + code: templateName + data_type: string + required: true + default: RockyLinux_9-16G + value_list: + - RockyLinux_9-16G + - OracleLinux_9-16G + - Ubuntu_24-20G + - Ubuntu_22-20G + descr: ОС + man: ОС + sort: 60 + - id: 177 + code: accessIpList + data_type: string + required: true + default: 79.111.119.78/32 185.247.187.1/32 + descr: Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов + man: IP адреса в CIDR формате через пробел для настройки аксес листов + sort: 70 + is_modifiable: true + - id: 406 + code: ipSpaceName + data_type: string + required: true + descr: Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) + - name: delete + id: 29 + kind: instance + action: delete + params: [] + - name: modify + id: 68 + kind: instance + action: modify + man: Операция автоматически останавливает ВМ. + params: + - id: 186 + code: vmCpu + data_type: integer > 0 + required: false + descr: CPU + man: CPU + sort: 10 + - id: 187 + code: vmRam + data_type: integer > 0 + required: false + descr: RAM + man: RAM + sort: 20 + - id: 188 + code: vmDisksize + data_type: integer > 0 + required: false + descr: Размер диска под сервис (GB) + man: Размер диска под сервис (GB) + sort: 30 + - id: 189 + code: accessIpList + data_type: string + required: false + descr: IP адреса в CIDR формате через пробел для настройки аксес листов + man: IP адреса в CIDR формате через пробел для настройки аксес листов + sort: 40 + - name: resume + id: 28 + kind: instance + action: resume + params: [] + - name: suspend + id: 27 + kind: instance + action: suspend + params: [] diff --git a/generated/test/resources_yaml/25_vcexternalip.yaml b/generated/test/resources_yaml/25_vcexternalip.yaml new file mode 100644 index 0000000..eaa29b4 --- /dev/null +++ b/generated/test/resources_yaml/25_vcexternalip.yaml @@ -0,0 +1,162 @@ +name: vcexternalip +service_id: 25 +service_display_name: Публичные IP адреса +service_short_name: vcexternalip +service_man: '**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса.' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 56 + kind: instance + action: create + params: + - id: 140 + code: serviceUid + data_type: string + required: true + sort: 20 + is_modifiable: true + - id: 141 + code: dnatCreate + data_type: boolean + required: true + default: "true" + descr: Необходимо создать DNAT-правило. + sort: 40 + is_modifiable: true + - id: 142 + code: internalPortAccess + data_type: string + required: true + default: "5432" + descr: Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
+ sort: 60 + is_modifiable: true + - id: 143 + code: snatCreate + data_type: boolean + required: false + descr: Необходимо создать SNAT-правило.
+ sort: 50 + is_modifiable: true + - id: 144 + code: internalAddrAccess + data_type: string + required: true + descr: Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true + sort: 70 + is_modifiable: true + - id: 324 + code: fromServiceNamespace + data_type: string + required: true + descr: Namespace составной услуги (для отображения правил FW) + sort: 80 + - id: 325 + code: fromServiceCloudEdgeName + data_type: string + required: false + descr: Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 90 + - id: 326 + code: fromServiceCloudVdcName + required: false + descr: Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 100 + - id: 327 + code: fromServiceCloudOrgName + data_type: string + required: false + descr: Org составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 110 + - id: 328 + code: fromServiceCloudVmwareUrl + data_type: string + required: false + descr: Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) + sort: 120 + - id: 336 + code: ipSpaceName + data_type: string + required: true + descr: Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) + sort: 30 + is_modifiable: true + - id: 488 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 200 + - id: 624 + code: fromServiceCloudEdgeScope + data_type: string + required: false + sort: 130 + - id: 626 + code: fromServiceVdcGroupName + data_type: string + required: false + sort: 140 + - name: delete + id: 57 + kind: instance + action: delete + params: [] + - name: modify + id: 197 + kind: instance + action: modify + params: + - id: 627 + code: serviceUid + data_type: string + required: true + sort: 20 + - id: 628 + code: ipSpaceName + data_type: string + required: true + sort: 30 + - id: 629 + code: dnatCreate + data_type: boolean + required: true + sort: 40 + - id: 630 + code: snatCreate + data_type: boolean + required: true + sort: 50 + - id: 631 + code: internalPortAccess + data_type: string + required: true + sort: 60 + - id: 632 + code: internalAddrAccess + data_type: string + required: true + sort: 70 diff --git a/provider/resources_yaml/26_vapp.yaml b/generated/test/resources_yaml/26_vapp.yaml similarity index 100% rename from provider/resources_yaml/26_vapp.yaml rename to generated/test/resources_yaml/26_vapp.yaml diff --git a/generated/test/resources_yaml/27_vc_vm_v2.yaml b/generated/test/resources_yaml/27_vc_vm_v2.yaml new file mode 100644 index 0000000..2b1d235 --- /dev/null +++ b/generated/test/resources_yaml/27_vc_vm_v2.yaml @@ -0,0 +1,71 @@ +name: vc_vm_v2 +service_id: 27 +service_display_name: VM в Cloud Director (vc_vmV2) +service_short_name: vc_vm_v2 +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 100 + kind: instance + action: create + params: + - id: 301 + code: serverListJson + data_type: json + required: true + default: '[{}]' + maxlength: 65536 + minlength: 10 + descr: Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) + sort: 20 + is_modifiable: true + - name: delete + id: 101 + kind: instance + action: delete + params: [] + - name: modify + id: 104 + kind: instance + action: modify + params: + - id: 302 + code: serverListJson + data_type: json + required: true + default: '[{}]' + maxlength: 65536 + minlength: 10 + descr: Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) + sort: 10 + - name: resume + id: 102 + kind: instance + action: resume + params: [] + - name: suspend + id: 103 + kind: instance + action: suspend + params: [] diff --git a/provider/resources_yaml/28_vc_vm_v3.yaml b/generated/test/resources_yaml/28_vc_vm_v3.yaml similarity index 100% rename from provider/resources_yaml/28_vc_vm_v3.yaml rename to generated/test/resources_yaml/28_vc_vm_v3.yaml diff --git a/provider/resources_yaml/29_vc_vdc_group.yaml b/generated/test/resources_yaml/29_vc_vdc_group.yaml similarity index 100% rename from provider/resources_yaml/29_vc_vdc_group.yaml rename to generated/test/resources_yaml/29_vc_vdc_group.yaml diff --git a/provider/resources_yaml/2_template.yaml b/generated/test/resources_yaml/2_template.yaml similarity index 100% rename from provider/resources_yaml/2_template.yaml rename to generated/test/resources_yaml/2_template.yaml diff --git a/provider/resources_yaml/32_vmpostgre.yaml b/generated/test/resources_yaml/32_vmpostgre.yaml similarity index 100% rename from provider/resources_yaml/32_vmpostgre.yaml rename to generated/test/resources_yaml/32_vmpostgre.yaml diff --git a/provider/resources_yaml/50_nextcloud.yaml b/generated/test/resources_yaml/50_nextcloud.yaml similarity index 100% rename from provider/resources_yaml/50_nextcloud.yaml rename to generated/test/resources_yaml/50_nextcloud.yaml diff --git a/generated/test/resources_yaml/81_superset.yaml b/generated/test/resources_yaml/81_superset.yaml new file mode 100644 index 0000000..e935cbe --- /dev/null +++ b/generated/test/resources_yaml/81_superset.yaml @@ -0,0 +1,104 @@ +name: superset +service_id: 81 +service_display_name: Apache Superset +service_short_name: superset +service_man: 'Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru)' +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 12 + kind: instance + action: create + man: DEV-Ветка сборки Superset + params: + - id: 14 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - id: 20 + code: emails + data_type: string + required: true + default: testuser1 + descr: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел + man: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел + sort: 70 + - id: 96 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро + man: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро + sort: 30 + - id: 97 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + man: Квота памяти пода (Указывается в Megabytes) + sort: 40 + - id: 98 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + - id: 109 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 227 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 60 + - name: delete + id: 13 + kind: instance + action: delete + man: DEV-Ветка удаления Superset + params: [] diff --git a/provider/resources_yaml/82_harbor.yaml b/generated/test/resources_yaml/82_harbor.yaml similarity index 100% rename from provider/resources_yaml/82_harbor.yaml rename to generated/test/resources_yaml/82_harbor.yaml diff --git a/generated/test/resources_yaml/88_k8s_ziti_controller.yaml b/generated/test/resources_yaml/88_k8s_ziti_controller.yaml new file mode 100644 index 0000000..7270f20 --- /dev/null +++ b/generated/test/resources_yaml/88_k8s_ziti_controller.yaml @@ -0,0 +1,90 @@ +name: k8s_ziti_controller +service_id: 88 +service_display_name: k8sZitiController +service_short_name: k8sZitiController +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 99 + kind: instance + action: create + params: + - id: 294 + code: resourceCPU + data_type: integer > 0 + required: true + default: "300" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (milicores) + sort: 30 + - id: 295 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (MBytes) + sort: 40 + - id: 296 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 50 + - id: 297 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 60 + - id: 298 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + sort: 20 + - id: 299 + code: domain + data_type: string + required: true + default: web01 + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm + sort: 10 + - name: delete + id: 105 + kind: instance + action: delete + params: [] diff --git a/provider/resources_yaml/89_flask.yaml b/generated/test/resources_yaml/89_flask.yaml similarity index 100% rename from provider/resources_yaml/89_flask.yaml rename to generated/test/resources_yaml/89_flask.yaml diff --git a/provider/resources_yaml/90_postgres.yaml b/generated/test/resources_yaml/90_postgres.yaml similarity index 100% rename from provider/resources_yaml/90_postgres.yaml rename to generated/test/resources_yaml/90_postgres.yaml diff --git a/provider/resources_yaml/91_redis.yaml b/generated/test/resources_yaml/91_redis.yaml similarity index 100% rename from provider/resources_yaml/91_redis.yaml rename to generated/test/resources_yaml/91_redis.yaml diff --git a/provider/resources_yaml/92_mongodb.yaml b/generated/test/resources_yaml/92_mongodb.yaml similarity index 100% rename from provider/resources_yaml/92_mongodb.yaml rename to generated/test/resources_yaml/92_mongodb.yaml diff --git a/generated/test/resources_yaml/93_rabbitmq.yaml b/generated/test/resources_yaml/93_rabbitmq.yaml new file mode 100644 index 0000000..50da006 --- /dev/null +++ b/generated/test/resources_yaml/93_rabbitmq.yaml @@ -0,0 +1,164 @@ +name: rabbitmq +service_id: 93 +service_display_name: RabbitMQ +service_short_name: rabbitmq +service_man: '# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

---' +lifecycle: + suspend_on_destroy_default: true + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 38 + kind: instance + action: create + params: + - id: 116 + code: resourceCPU + data_type: integer > 0 + required: true + default: "1000" + maxvalue: 4000 + minvalue: 500 + descr: 'Пример: `2000`' + sort: 20 + is_modifiable: true + - id: 117 + code: resourceMemory + data_type: integer > 0 + required: true + default: "1124" + maxvalue: 8192 + minvalue: 512 + descr: 'Пример: `2048`' + sort: 30 + is_modifiable: true + - id: 118 + code: resourceDisk + data_type: integer > 0 + required: true + default: "30" + maxvalue: 999 + minvalue: 5 + descr: 'Пример: `20`' + sort: 40 + is_modifiable: true + - id: 119 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Выбрать платформу из списка + sort: 10 + - id: 224 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + sort: 50 + is_modifiable: true + - id: 225 + code: needExternalAddressMaster + data_type: boolean + required: true + default: "false" + value_list: + - "false" + - "true" + descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`' + sort: 60 + is_modifiable: true + - id: 543 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Пример: `internet-no-antiddos-v1`' + is_modifiable: true + - name: delete + id: 39 + kind: instance + action: delete + params: [] + - name: modify + id: 106 + kind: instance + action: modify + params: + - id: 304 + code: resourceCPU + data_type: integer > 0 + required: true + maxvalue: 4000 + minvalue: 500 + descr: 'Пример: `2000`' + sort: 10 + - id: 305 + code: resourceMemory + data_type: integer > 0 + required: true + maxvalue: 8192 + minvalue: 512 + descr: 'Пример: `2048`' + sort: 20 + - id: 306 + code: resourceDisk + data_type: integer > 0 + required: true + maxvalue: 999 + minvalue: 1 + descr: 'Пример: `20`' + sort: 10 + - id: 307 + code: resourceInstances + data_type: integer > 0 + required: true + maxvalue: 7 + minvalue: 1 + descr: 'Пример: `1`' + sort: 40 + - id: 308 + code: needExternalAddressMaster + data_type: boolean + required: true + value_list: + - "false" + - "true" + descr: Выделяет внешний (белый) IP для доступа к master + sort: 50 + - id: 545 + code: ipSpaceNameMaster + data_type: string + required: false + descr: 'Пример: `internet-no-antiddos-v1`' + - name: resume + id: 117 + kind: instance + action: resume + params: [] + - name: suspend + id: 116 + kind: instance + action: suspend + man: Пока не работает из за того что RabbitmqCluster нельзя занулить + params: [] diff --git a/provider/resources_yaml/94_lucee.yaml b/generated/test/resources_yaml/94_lucee.yaml similarity index 100% rename from provider/resources_yaml/94_lucee.yaml rename to generated/test/resources_yaml/94_lucee.yaml diff --git a/provider/resources_yaml/95_nodejs.yaml b/generated/test/resources_yaml/95_nodejs.yaml similarity index 100% rename from provider/resources_yaml/95_nodejs.yaml rename to generated/test/resources_yaml/95_nodejs.yaml diff --git a/provider/resources_yaml/96_pgadmin.yaml b/generated/test/resources_yaml/96_pgadmin.yaml similarity index 100% rename from provider/resources_yaml/96_pgadmin.yaml rename to generated/test/resources_yaml/96_pgadmin.yaml diff --git a/generated/test/resources_yaml/97_nodered.yaml b/generated/test/resources_yaml/97_nodered.yaml new file mode 100644 index 0000000..4edaed8 --- /dev/null +++ b/generated/test/resources_yaml/97_nodered.yaml @@ -0,0 +1,91 @@ +name: nodered +service_id: 97 +service_display_name: NodeRed +service_short_name: nodered +lifecycle: + suspend_on_destroy_default: false + adopt_existing_on_create_default: false +outputs: + params: + - code: state_params + type: map + - code: state_out + type: map + - code: state_params_flat + type: map + - code: state_out_flat + type: map + - code: vault_secrets + type: map + sensitive: true + - code: vault_url + type: string + - code: vault_user_path + type: string + - code: vault_fields + type: list +operations: + - name: create + id: 72 + kind: instance + action: create + params: + - id: 193 + code: resourceCPU + data_type: integer > 0 + required: true + default: "500" + maxvalue: 4000 + minvalue: 100 + descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро + sort: 30 + - id: 194 + code: resourceMemory + data_type: integer > 0 + required: true + default: "512" + maxvalue: 8192 + minvalue: 128 + descr: Квота памяти пода (Указывается в Megabytes) + sort: 40 + - id: 195 + code: resourceDisk + data_type: integer > 0 + required: true + default: "1" + maxvalue: 999 + minvalue: 1 + descr: Квота диска (Указывается в GIGAbytes) + man: Квота диска (Указывается в GIGAbytes) + sort: 60 + - id: 196 + code: resourceRealm + data_type: string + required: true + func: getAvailableResourceRealms + descr: Платформа для развертывания + man: Наименование кластера Kubernetes для подключения + sort: 20 + - id: 197 + code: domain + data_type: string + required: true + regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ + unique_scope: provider + descr: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru + man: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru + sort: 10 + - id: 323 + code: resourceInstances + data_type: integer > 0 + required: true + default: "1" + maxvalue: 7 + minvalue: 1 + descr: Количество узлов + sort: 50 + - name: delete + id: 73 + kind: instance + action: delete + params: [] diff --git a/provider/resources_yaml/98_http.yaml b/generated/test/resources_yaml/98_http.yaml similarity index 100% rename from provider/resources_yaml/98_http.yaml rename to generated/test/resources_yaml/98_http.yaml diff --git a/provider/resources_yaml/99_gitea.yaml b/generated/test/resources_yaml/99_gitea.yaml similarity index 100% rename from provider/resources_yaml/99_gitea.yaml rename to generated/test/resources_yaml/99_gitea.yaml diff --git a/generated/test/resources_yaml/embed.go b/generated/test/resources_yaml/embed.go new file mode 100644 index 0000000..72e3ff2 --- /dev/null +++ b/generated/test/resources_yaml/embed.go @@ -0,0 +1,8 @@ +package resources_yaml + +import "embed" + +// Files embeds unified service YAML specs for provider runtime lookup. +// +//go:embed *.yaml +var Files embed.FS diff --git a/generated/test/show_inventory.py b/generated/test/show_inventory.py new file mode 100644 index 0000000..f9508e4 --- /dev/null +++ b/generated/test/show_inventory.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +import json, glob + +for f in sorted(glob.glob("/home/naeel/terra/terraform/devops/profiles/test/generated/output_inventory/svc_*.json")): + d = json.load(open(f)) + params = d.get("params", {}) + out = d.get("out", {}) + if params or out: + print(f.split("/")[-1], "params:", list(params.keys()), "out:", list(out.keys())) + if "domain" in params: + print(f" DOMAIN={params['domain']}") diff --git a/generated/test/tmp/.gitkeep b/generated/test/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/provider/.gitignore b/provider/.gitignore index e660fd9..af9814b 100644 --- a/provider/.gitignore +++ b/provider/.gitignore @@ -1 +1,3 @@ bin/ +resources_yaml/ +internal/resources_gen/ diff --git a/provider/internal/resources_gen/50_nextcloud_reconcile_action.go b/provider/internal/resources_gen/50_nextcloud_reconcile_action.go deleted file mode 100644 index b97c8e1..0000000 --- a/provider/internal/resources_gen/50_nextcloud_reconcile_action.go +++ /dev/null @@ -1,136 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Action: reconcile - -var _ resource.Resource = &NextcloudReconcileResource{} - -// NextcloudReconcileResource triggers a one-time action. -type NextcloudReconcileResource struct { - client *core.UniversalClient -} - -type NextcloudReconcileModel struct { - ID types.String `tfsdk:"id"` - NextcloudID types.String `tfsdk:"nextcloud_id"` - RunID types.String `tfsdk:"run_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewNextcloudReconcileResource() resource.Resource { - return &NextcloudReconcileResource{} -} - -func (r *NextcloudReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud_reconcile" -} - -func (r *NextcloudReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true}, - "nextcloud_id": schema.StringAttribute{Required: true}, - "run_id": schema.StringAttribute{Required: true}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan NextcloudReconcileModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - runID := strings.TrimSpace(plan.RunID.ValueString()) - if instanceUID == "" || runID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") - return - } - - params := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudReconcileModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudReconcileModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - runID := strings.TrimSpace(plan.RunID.ValueString()) - if instanceUID == "" || runID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") - return - } - - params := map[string]string{} - - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // No-op: removing the resource does not trigger a remote action. -} - -func (r *NextcloudReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/50_nextcloud_recovery_action.go b/provider/internal/resources_gen/50_nextcloud_recovery_action.go deleted file mode 100644 index e62142d..0000000 --- a/provider/internal/resources_gen/50_nextcloud_recovery_action.go +++ /dev/null @@ -1,170 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Action: recovery - -var _ resource.Resource = &NextcloudRecoveryResource{} - -// NextcloudRecoveryResource triggers a one-time action. -type NextcloudRecoveryResource struct { - client *core.UniversalClient -} - -type NextcloudRecoveryModel struct { - ID types.String `tfsdk:"id"` - NextcloudID types.String `tfsdk:"nextcloud_id"` - RunID types.String `tfsdk:"run_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` - ClusterConfiguration types.String `tfsdk:"cluster_configuration"` - AccessConfiguration types.String `tfsdk:"access_configuration"` - AppConfiguration types.String `tfsdk:"app_configuration"` -} - -func NewNextcloudRecoveryResource() resource.Resource { - return &NextcloudRecoveryResource{} -} - -func (r *NextcloudRecoveryResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud_recovery" -} - -func (r *NextcloudRecoveryResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true}, - "nextcloud_id": schema.StringAttribute{Required: true}, - "run_id": schema.StringAttribute{Required: true}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true}, - "cluster_configuration": schema.StringAttribute{Required: true}, - "access_configuration": schema.StringAttribute{Required: true}, - "app_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Версия Nextcloud будет взята из бэкапа"}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudRecoveryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan NextcloudRecoveryModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") - return - } - if plan.ClusterConfiguration.IsNull() || plan.ClusterConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "cluster_configuration is required.") - return - } - if plan.AccessConfiguration.IsNull() || plan.AccessConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "access_configuration is required.") - return - } - if plan.AppConfiguration.IsNull() || plan.AppConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "app_configuration is required.") - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - runID := strings.TrimSpace(plan.RunID.ValueString()) - if instanceUID == "" || runID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") - return - } - - params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - "clusterConfiguration": resources_core.FormatString(plan.ClusterConfiguration), - "accessConfiguration": resources_core.FormatString(plan.AccessConfiguration), - "appConfiguration": resources_core.FormatString(plan.AppConfiguration), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "recovery", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "recovery", runID)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudRecoveryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudRecoveryModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudRecoveryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudRecoveryModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - runID := strings.TrimSpace(plan.RunID.ValueString()) - if instanceUID == "" || runID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") - return - } - - params := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - "clusterConfiguration": resources_core.FormatString(plan.ClusterConfiguration), - "accessConfiguration": resources_core.FormatString(plan.AccessConfiguration), - "appConfiguration": resources_core.FormatString(plan.AppConfiguration), - } - - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "recovery", params); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "recovery", runID)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudRecoveryResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // No-op: removing the resource does not trigger a remote action. -} - -func (r *NextcloudRecoveryResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/provider/internal/resources_gen/90_postgres_reconcile_action.go b/provider/internal/resources_gen/90_postgres_reconcile_action.go deleted file mode 100644 index 5a9e4f6..0000000 --- a/provider/internal/resources_gen/90_postgres_reconcile_action.go +++ /dev/null @@ -1,136 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Action: reconcile - -var _ resource.Resource = &PostgresReconcileResource{} - -// PostgresReconcileResource triggers a one-time action. -type PostgresReconcileResource struct { - client *core.UniversalClient -} - -type PostgresReconcileModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - RunID types.String `tfsdk:"run_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewPostgresReconcileResource() resource.Resource { - return &PostgresReconcileResource{} -} - -func (r *PostgresReconcileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_reconcile" -} - -func (r *PostgresReconcileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true}, - "postgres_id": schema.StringAttribute{Required: true}, - "run_id": schema.StringAttribute{Required: true}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresReconcileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresReconcileModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - runID := strings.TrimSpace(plan.RunID.ValueString()) - if instanceUID == "" || runID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") - return - } - - params := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "reconcile", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresReconcileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresReconcileModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresReconcileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresReconcileModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - runID := strings.TrimSpace(plan.RunID.ValueString()) - if instanceUID == "" || runID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра или run_id") - return - } - - params := map[string]string{} - - if err := resources_core.RunOperationByCode(ctx, r.client, instanceUID, "reconcile", params); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "reconcile", runID)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresReconcileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // No-op: removing the resource does not trigger a remote action. -} - -func (r *PostgresReconcileResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -}