feat(test-stand): TEST_STAND/FPipeGmail — копия dev-примера под test-стенд

Создана папка (без .terraform/state/lock — это состояние dev). Изменены параметры:
- versions.tf: nubes-dev/nubes 2.0.1 -> nubes-test/nubes 3.0.0;
- variables.tf: api_endpoint -> lk-api-gateway-test.ngcloud.ru;
- terraform.tfvars: organization=NaeelOrg (найдена через API, svc 19), ip_count=10
  (в test на орге уже выделено 10 IP), storage SSD (в test vDC строят на SSD);
- shturval.tf: shturval-test1 / shturval-test-01 / workers-shturval-test.

Коллизии проверены: в test заняты naeel-vdc, 'VDC для кластера iot-naeel',
naeel_vc_nsxt, 'Edge для кластера IOT naeel', 'Кластер Kubernetes [iot-naeel]' —
наши имена (fullpipe-vdc/-edge/-vapp-02, web02, shturval-test1) свободны.

Проверено: terraform init (3.0.0), fmt -check OK, validate Success,
plan = 7 to add / 0 change / 0 destroy. apply НЕ запускался.

Документация: HISTORY/2026-10-01_test_stand_fpipegmail.md
This commit is contained in:
Repinoid
2026-10-01 08:21:08 +03:00
parent 8eae7f383d
commit f7d6eb4688
11 changed files with 720 additions and 0 deletions
@@ -0,0 +1,79 @@
# 2026-10-01 — Стенд `TEST_STAND/FPipeGmail`: копия dev-примера, адаптированная под test
> Команда владельца: «`DEV_STAND/FPipeGmail` — это в дев. Надо — сделать такую же папку
> в `TEST_STAND` и соответственно изменить параметры в tf-конфигах, и посмотри чтобы
> не было коллизий — там в стенде есть и эдж и вдц. Найди имя огра».
## Что сделано
Создана `TEST_STAND/FPipeGmail/` — копия `DEV_STAND/FPipeGmail` **без** `.terraform/`,
`terraform.tfstate*`, `.terraform.lock.hcl` (это состояние dev, к test оно не относится).
Состав: `vdc.tf`, `edge.tf`, `modifiers.tf`, `vm.tf`, `shturval.tf`, `variables.tf`,
`outputs.tf`, `provider.tf`, `versions.tf`, `terraform.tfvars` (+ `.example`).
## Найденные данные test-стенда (из API, не из догадок)
| Что | Значение | Откуда |
|---|---|---|
| Организация (vcOrg) | **`NaeelOrg`**, uuid `3f0850f2-3506-4efd-b84b-7270b5027ab5`, `running`, `iaas` | `GET /instances?serviceId=19` |
| ipSpace на организации | `internet-ipv4-v1`, уже выделено **10** IP | `GET /instances/3f0850f2-…` → `vIPConfigure` |
| networkProvider | `snb1` | `GET /instances/e3c9e4f1-…` (существующий vDC `naeel-vdc`) |
| providerVdc | `Intel Broadwell 2.4` | там же |
| storage | `SSD` | там же (`storageConfig`) |
| realm | `sandbox.nubes.ru` | `GET /instances/3f0850f2-…` |
## Изменённые параметры (dev → test)
| Файл | Было (dev) | Стало (test) |
|---|---|---|
| `versions.tf` | `…/nubes-dev/nubes`, `2.0.1` | **`…/nubes-test/nubes`, `3.0.0`** |
| `variables.tf` → `api_endpoint` | `lk-api-gateway-dev…` | **`lk-api-gateway-test…`** |
| `terraform.tfvars` → `organization` | `kontra` | **`NaeelOrg`** |
| `terraform.tfvars` → `api_token` | dev-токен | **test-токен** (`secrets/test.token`) |
| `terraform.tfvars` → `ip_count` | `4` | **`10`** (не уменьшать уже выделенное) |
| `terraform.tfvars` → `vdc_storage_config` | `SATA / 200` | **`SSD / 200`** (в test vDC строят на SSD) |
| `shturval.tf` → `shturval_resource_name` | `shturval-dev1` | **`shturval-test1`** |
| `shturval.tf` → `shturval_cluster_name` | `shturval-dev-01` | **`shturval-test-01`** |
| `shturval.tf` → `shturval_worker_group_name` | `workers-shturval-dev` | **`workers-shturval-test`** |
Не менялись (совпадают с test): `vdc_network_provider=snb1`, `vdc_provider_vdc="Intel Broadwell 2.4"`,
`ip_space_name=internet-ipv4-v1`, DNS/пул Edge, имена `fullpipe-vdc` / `fullpipe-edge` / `fullpipe-vapp-02`.
## Проверка коллизий (в test уже есть эдж и vDC)
Занято в test сейчас:
| Услуга | Имена | Статус |
|---|---|---|
| vDC (21) | `naeel-vdc`, `VDC для кластера iot-naeel` | running |
| Edge (22) | `naeel_vc_nsxt`, `Edge для кластера IOT naeel` | running |
| vApp (26) | `vapp-222`, `vm-sless-vapp`, `vm-sless-demo-vapp` | deleted |
| ВМ (28) | `vm-sless`, `vm-sless-1`, `vm-sless-demo` | deleted |
| Штурвал (150) | `naeel-wheel` (deleted), `Кластер Kubernetes [iot-naeel]` | running |
Наши имена (`fullpipe-vdc`, `fullpipe-edge`, `fullpipe-vapp-02`, `web02`, `shturval-test1`)
**свободны** — пересечений нет.
## Проверки конфигурации
```bash
terraform init # установлен nubes-test/nubes 3.0.0 (подпись CB3A0DF161ECC416)
terraform fmt -check -recursive # OK
terraform validate # Success! The configuration is valid.
terraform plan # Plan: 7 to add, 0 to change, 0 to destroy
```
⚠️ `terraform apply` **не выполнялся** — по правилам запускает только владелец.
## Риск, который надо помнить
- `nubes_vc_org_ip_allocation` (модификатор `vIPConfigure`) **перезаписывает массив внешних IP целиком**.
В test на `NaeelOrg` уже выделено 10 адресов (их использует кластер `iot-naeel`), поэтому
`ip_count="10"` — уменьшение сломает чужой стенд.
- В `.gitignore` уже закрыты `terraform.tfvars`, `.terraform/`, `.terraform.lock.hcl`,
`terraform.tfstate*` — в репозиторий попадут только `.tf` и `terraform.tfvars.example`.
## Связанные документы
- `HISTORY/2026-09-25_fullpipe_example_shturval_and_docs.md` — исходный пример FPipeGmail (dev).
- `HISTORY/2026-10-01_release_x_0_0_all_stands.md` — актуальные версии провайдеров стендов.
+28
View File
@@ -0,0 +1,28 @@
resource "nubes_vc_nsxt" "edge" {
resource_name = var.nsxt_resource_name
# Тип родительской услуги: "vdc" (нужен vdc_uid) или "vdcGroup" (нужен vdc_group_uid)
vdc_type = var.nsxt_vdc_type
# refSvc-поле: принимает UUID или имя. Здесь берём UID созданного VDC,
# чтобы Edge гарантированно создавался после vDC.
vdc_uid = nubes_vc_vdc.vdc.id
need_enable_avi = var.nsxt_need_enable_avi
virtual_services_count = var.nsxt_virtual_services_count
# routed-сеть, которую разворачивает Edge (SingleNestedAttribute -> объект)
routed_net_configuration = {
ip_addr_pool = var.nsxt_ip_addr_pool
main_dns = var.nsxt_main_dns
second_dns = var.nsxt_second_dns
}
# «Заморозка»: destroy НЕ удаляет эдж (у платформы для эджа нет операции suspend),
# а только убирает его из состояния. Для полного удаления — keep_on_destroy = false.
keep_on_destroy = true
# Повторный apply усыновляет уже работающий эдж, а не падает с
# «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (RUNNING)».
adopt_existing_on_create = true
}
+60
View File
@@ -0,0 +1,60 @@
# =============================================================================
# Ресурсы-модификаторы (операции modify, которых нет в create-схеме ресурсов)
#
# Порядок строго такой:
# орга (создана вручную в ЛК)
# -> nubes_vc_vdc.vdc
# -> nubes_vc_nsxt.edge
# -> nubes_vc_org_ip_allocation (выделение внешних IP на орге)
# -> nubes_vc_nsxt_snat (SNAT на эдже этим ipSpace)
#
# Почему аллокация ПОСЛЕ эджа: платформа строит список ipSpace из состояния
# `job.vcd.networkProvider` / `job.vcd.providerGateway`, то есть требует уже
# созданный vDC и Edge. Иначе modify на орге падает
# («Can't cast Complex Object Type Struct to String»).
# =============================================================================
# 1. Внешние IP на организации (modify: vIPConfigure, массив перезаписывается целиком)
resource "nubes_vc_org_ip_allocation" "org_ip" {
organization = var.organization
vip_configure = jsonencode([
{
name = var.ip_space_name
count = var.ip_count
}
])
# true = «заморозка»: destroy не трогает квоту внешних IP (кластер Штурвала держит
# адреса, опустить count ниже занятых платформа не даёт). Для полного удаления — false
# (и только после удаления кластера).
keep_on_destroy = true
depends_on = [nubes_vc_nsxt.edge]
}
# 2. SNAT на эдже (modify: ipSpaceName)
resource "nubes_vc_nsxt_snat" "snat" {
nsxt_uid = nubes_vc_nsxt.edge.id
ip_space_name = var.ip_space_name
# true = «заморозка»: destroy не выключает SNAT на эдже. Для полного удаления — false.
keep_on_destroy = true
# ipSpace должен быть уже выделен на организации
depends_on = [nubes_vc_org_ip_allocation.org_ip]
}
output "allocated_org_ip" {
description = "Выделено внешних IP на организации"
value = {
organization = var.organization
ip_space_name = var.ip_space_name
ip_count = var.ip_count
}
}
output "snat_ip_space" {
description = "ipSpace, включённый как SNAT на эдже"
value = nubes_vc_nsxt_snat.snat.ip_space_name
}
+29
View File
@@ -0,0 +1,29 @@
output "vdc_id" {
description = "UID созданного VDC"
value = nubes_vc_vdc.vdc.id
}
output "vdc_name" {
description = "Имя VDC"
value = nubes_vc_vdc.vdc.resource_name
}
output "vdc_state_params" {
description = "Параметры состояния VDC из API"
value = nubes_vc_vdc.vdc.state_params
}
output "nsxt_id" {
description = "UID созданного Edge (vc_nsxt)"
value = nubes_vc_nsxt.edge.id
}
output "nsxt_name" {
description = "Имя Edge (vc_nsxt)"
value = nubes_vc_nsxt.edge.resource_name
}
output "nsxt_state_params" {
description = "Параметры состояния Edge (vc_nsxt) из API"
value = nubes_vc_nsxt.edge.state_params
}
+4
View File
@@ -0,0 +1,4 @@
provider "nubes" {
api_token = var.api_token
api_endpoint = var.api_endpoint
}
+161
View File
@@ -0,0 +1,161 @@
# =============================================================================
# Kubernetes кластер Штурвал — сервис 150, ресурс nubes_k8s_sthutrval_cluster
# (НЕ 148 «Менеджмент Kubernetes кластер Штурвал» — это другой сервис)
#
# Всё, что относится к Штурвалу, лежит ТОЛЬКО в этом файле: переменные, их
# значения по умолчанию и сам ресурс. Чтобы выключить Штурвал — удалить файл
# или закомментировать ресурс.
#
# Порядок (чек-лист из инструкции на услугу в ЛК):
# 1) Организация в Cloud Director — создана вручную в ЛК
# 2) nubes_vc_vdc.vdc — есть
# 3) nubes_vc_nsxt.edge — есть, обязательно ALB + AVI VS >= 3
# 4) внешние адреса в организации — суммарно >= 3 (nubes_vc_org_ip_allocation)
# 5) SNAT на Edge — nubes_vc_nsxt_snat
# 6) Kubernetes кластер Штурвал — этот ресурс
#
# Минимальные требования к кластеру: мастер-нод >= 1, воркер-нод >= 1,
# 4 vCPU / 8 GB RAM / 50 GB диска на ноду.
# =============================================================================
# --- Переменные Штурвала ---
variable "shturval_resource_name" {
type = string
default = "shturval-test1"
description = "Имя услуги «Kubernetes кластер Штурвал» в ЛК"
}
variable "shturval_cluster_name" {
type = string
default = "shturval-test-01"
description = "Имя кластера внутри Штурвала"
}
variable "shturval_app_version" {
type = string
default = "2.14.0"
description = "Версия Штурвала (значение по умолчанию платформы — 2.14.0)"
}
variable "shturval_cp_sizing_policy" {
type = string
default = "TKG 4CPU 8RAM"
description = "Политика размера control plane: 4 vCPU / 8 GB (минимум по инструкции). Должна существовать в ресурсной платформе vDC — список политик берётся из услуги «Виртуальный датацентр»"
}
variable "shturval_cp_sizing_disk" {
type = number
default = 50
description = "Диск control plane, ГБ (минимум 50)"
}
variable "shturval_cp_count" {
type = number
default = 1
description = "Количество мастер-нод: 1, 3 или 5"
}
variable "shturval_worker_group_name" {
type = string
default = "workers-shturval-test"
description = "Имя группы воркеров (уникальное в кластере; допустимы строчные латинские буквы, цифры и дефис)"
}
variable "shturval_worker_sizing_policy" {
type = string
default = "TKG 4CPU 8RAM"
description = "Политика размера воркеров: 4 vCPU / 8 GB (минимум по инструкции)"
}
variable "shturval_worker_sizing_disk" {
type = number
default = 50
description = "Диск воркеров, ГБ (минимум 50)"
}
variable "shturval_worker_count" {
type = number
default = 1
description = "Количество воркер-нод (минимум 1)"
}
# --- Значения, которые собираются из переменных ---
locals {
# Группы воркеров передаются JSON-строкой ВНУТРЬ услуги как есть, поэтому ключи
# должны быть ровно такими, как в манифесте услуги 150: groupName, sizingPolicy,
# sizingDisk, count, autoscale, labelDeck.
# ВНИМАНИЕ: в сгенерированном примере провайдера (docs → Example) ключи показаны
# в snake_case — это ошибка генератора, платформа на них падает с
# «Cannot invoke method split() on null object» (не находит groupName → null).
shturval_worker_config = jsonencode([
{
groupName = var.shturval_worker_group_name
sizingPolicy = var.shturval_worker_sizing_policy
sizingDisk = var.shturval_worker_sizing_disk
count = var.shturval_worker_count
autoscale = false # автоскейл выключен
labelDeck = true # разрешить разворачивать услуги из ЛК на этих нодах
}
])
}
# --- Ресурс Штурвала ---
resource "nubes_k8s_sthutrval_cluster" "shturval" {
resource_name = var.shturval_resource_name
# Кластер Штурвала уже существует (инстанс «shturval-dev») и в проде не
# удаляется неделями, поэтому ресурс должен УСЫНОВИТЬ существующий инстанс,
# а не падать с «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (SUSPEND)».
# Проверка/adopt выполняются в Create на apply (в plan будет «will be created»).
adopt_existing_on_create = true
# «Заморозка»: destroy приостанавливает кластер (suspend), а не удаляет.
# Следующий apply усыновит его и разморозит (resume).
suspend_on_destroy = true
# Штурвал создаётся долго (десятки минут) — поднимаем таймаут ожидания,
# иначе провайдер сдаётся на дефолтных 600 с.
operation_timeout = "60m"
startup_configuration = {
# vDC и Edge из этого же конфига (обязательные поля)
vdc_uid = nubes_vc_vdc.vdc.id
nsxt_uid = nubes_vc_nsxt.edge.id
cluster_name = var.shturval_cluster_name
# Дополнительные возможности кластера (в ЛК — галочки при создании)
ex_logging = true # логи в Loki (без него логи услуг не видны в ЛК)
ex_monitoring = true # метрики в VictoriaMetrics (без него метрик в ЛК нет)
ex_local_csi = true
ex_vip = true
ex_update = true
ex_ingress = true
ex_named_csi = true
}
cluster_configuration = {
app_version = var.shturval_app_version
}
control_plane_configuration = {
sizing_policy = var.shturval_cp_sizing_policy
sizing_disk = var.shturval_cp_sizing_disk
count = var.shturval_cp_count
}
worker_configuration = local.shturval_worker_config
access_configuration = {
need_external_address_api = true # внешний адрес для Kubernetes API (false недопустим)
access_ip_list_api = jsonencode([]) # пусто = доступ всем
need_external_address_ingress = true # внешний адрес для Ingress
access_ip_list_ingress = jsonencode([]) # пусто = доступ всем
}
# Кластер поднимается только после готовой сети: vDC -> Edge -> внешние IP -> SNAT
depends_on = [nubes_vc_nsxt_snat.snat]
}
@@ -0,0 +1,24 @@
api_token = "ВАШ_ТОКЕН_ИЗ_ЛК (test)"
# Имя или UUID организации (test-стенд: NaeelOrg, uuid 3f0850f2-3506-4efd-b84b-7270b5027ab5)
organization = "NaeelOrg"
# Внешние IP на организации (modify vIPConfigure). ВНИМАНИЕ: массив
# перезаписывается целиком — значение не должно быть меньше уже занятого
# (в test на NaeelOrg уже выделено 10 → count="10").
ip_space_name = "internet-ipv4-v1"
ip_count = "10"
vdc_resource_name = "fullpipe-vdc"
vdc_network_provider = "snb1"
vdc_provider_vdc = "Intel Broadwell 2.4"
vdc_cpu_allocated = 12
vdc_cpu_guaranteed = 0
vdc_mem_allocated = 32
# Политика хранилища должна существовать в ресурсном пуле.
# В test существующий vDC (naeel-vdc) создан с политикой SSD.
vdc_storage_config = "[{\"name\":\"SSD\",\"size\":\"200\"}]"
# Всё про ВМ (включая публичный SSH-ключ) лежит внутри vm.tf —
# здесь для ВМ задавать ничего не нужно.
+116
View File
@@ -0,0 +1,116 @@
variable "api_token" {
type = string
sensitive = true
description = "API-токен Nubes"
}
variable "api_endpoint" {
type = string
default = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
description = "API Gateway URL"
}
# Имя (display_name, напр. "kontora") ИЛИ UUID организации из ЛК
variable "organization" {
type = string
description = "Имя или UUID организации (vc_org)"
}
# --- Модификаторы (IP на орге + SNAT на эдже) ---
variable "ip_space_name" {
type = string
description = "Имя ipSpace, доступное организации (смотреть в ЛК, напр. internet-ipv4-v1)"
}
variable "ip_count" {
type = string
default = "3"
description = "Сколько внешних IP выделить на организации (count — строка)"
}
variable "vdc_resource_name" {
type = string
default = "fullpipe-vdc"
description = "Имя VDC"
}
variable "vdc_network_provider" {
type = string
default = null
description = "Сетевой провайдер. Заполнить значением из текущей страницы ЛК"
}
variable "vdc_provider_vdc" {
type = string
default = null
description = "Provider VDC. Заполнить значением из текущей страницы ЛК"
}
variable "vdc_cpu_allocated" {
type = number
default = 8
description = "vCPU (шт.)"
}
variable "vdc_cpu_guaranteed" {
type = number
default = 0
description = "Резервирование vCPU (%, допустимо: 0, 50, 80)"
}
variable "vdc_mem_allocated" {
type = number
default = 32
description = "RAM (GB)"
}
variable "vdc_storage_config" {
type = string
default = "[{\"name\":\"SATA\",\"size\":\"200\"}]"
description = "Дисковое хранилище (JSON-массив, size в GB). Имя политики должно существовать в ресурсном пуле (например, SATA, SSD)"
}
# --- vc_nsxt (Сетевой шлюз периметра / Edge) ---
variable "nsxt_resource_name" {
type = string
default = "fullpipe-edge"
description = "Имя Edge (vc_nsxt)"
}
variable "nsxt_vdc_type" {
type = string
default = "vdc"
description = "Тип родительской услуги: vdc или vdcGroup"
}
variable "nsxt_need_enable_avi" {
type = bool
default = true
description = "Включить AVI Load Balancer (ALB)"
}
variable "nsxt_virtual_services_count" {
type = number
default = 3
description = "Кол-во виртуальных сервисов на AVI (1..4; Штурвал: ≥ 3)"
}
variable "nsxt_ip_addr_pool" {
type = string
default = "10.10.102.0/24"
description = "Адресный пул routed-сети (маска /24 обязательна)"
}
variable "nsxt_main_dns" {
type = string
default = "81.22.46.22"
description = "Основной DNS"
}
variable "nsxt_second_dns" {
type = string
default = "185.247.187.77"
description = "Второй DNS"
}
+20
View File
@@ -0,0 +1,20 @@
resource "nubes_vc_vdc" "vdc" {
resource_name = var.vdc_resource_name
# Организация: имя из ЛК ("kontora") или точный UUID
organization_uid = var.organization
network_provider = var.vdc_network_provider
provider_vdc = var.vdc_provider_vdc
cpu_allocated = var.vdc_cpu_allocated
cpu_guaranteed = var.vdc_cpu_guaranteed
mem_allocated = var.vdc_mem_allocated
# JSON-массив дисковых политик (size в GB)
storage_config = var.vdc_storage_config
suspend_on_destroy = true
adopt_existing_on_create = true
}
+10
View File
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.5.0"
required_providers {
nubes = {
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-test/nubes"
version = "3.0.0"
}
}
}
+189
View File
@@ -0,0 +1,189 @@
# =============================================================================
# Виртуальная машина внутри vApp — услуги 26 (vApp) и 28 (ВМ)
#
# Всё, что относится к ВМ, лежит ТОЛЬКО в этом файле: переменные, их значения
# по умолчанию, оба ресурса и выводы. Чтобы выключить ВМ — удалить или
# закомментировать этот файл (по аналогии с shturval.tf).
#
# Место в цепочке:
# орга (вручную в ЛК) → vDC (21) → Edge (22) → внешние IP → SNAT
# → [ vApp (26) → ВМ (28) ] → Штурвал (150)
#
# Зависимости (из манифестов услуг, сгенерированные ресурсы):
# vApp (26) — nubes_vapp: требует vdc_uid (21) и nsxt_uid (22)
# ВМ (28) — nubes_vc_vm_v3: требует vapp_uid (26)
#
# Внешний доступ: ВМ публикуется за общим SNAT эджа (same_snat = false), для
# этого ipSpace должен быть выделен на организации и включён как SNAT
# (см. modifiers.tf). Нужен ВЫДЕЛЕННЫЙ внешний адрес — same_snat = true.
# ipSpace для ВМ берём тот же, что у SNAT (var.ip_space_name).
#
# Режим destroy: у обеих услуг операция delete требует предварительного
# suspend, поэтому по умолчанию suspend_on_destroy = true («заморозка»).
# Полное удаление vApp возможно только через 14 дней после suspend.
# =============================================================================
# --- Переменные vApp ---
variable "vapp_resource_name" {
type = string
default = "fullpipe-vapp-02"
description = "Имя услуги «Виртуальный каталог ВМ (vApp)» в ЛК"
}
variable "vapp_name" {
type = string
default = "fullpipe-vapp-02"
description = "Имя vApp. Маска ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$, уникально в организации; участвует в DNS-имени ВМ. НЕ оставлять дефолтом платформы."
}
# --- Переменные ВМ ---
variable "vm_resource_name" {
type = string
default = "fullpipe-vm-02"
description = "Имя услуги «Виртуальная машина» в ЛК"
}
variable "vm_name" {
type = string
default = "web02"
description = "Имя ВМ. Маска ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$. Определяет имя NSX-T IP Set: {vapp_name}-{vm_name}"
}
variable "vm_image" {
type = string
default = "Ubuntu_22-20G"
description = "Образ ОС. Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G. Не изменяется после создания"
}
variable "vm_cpu" {
type = number
default = 2
description = "vCPU (1..64), шт"
}
variable "vm_ram" {
type = number
default = 2
description = "RAM (1..256), GB"
}
variable "vm_disk" {
type = number
default = 20
description = "Дополнительный диск, GB (основной диск зависит от образа)"
}
variable "vm_user_login" {
type = string
default = "ubuntu"
description = "Учётка SSH. Не изменяется после создания"
}
variable "vm_user_public_key" {
type = string
# ВСЕ параметры ВМ живут в этом файле — включая ключ. Удалил файл — ВМ исключена
# из конфига полностью, в terraform.tfvars ничего про ВМ не остаётся.
# Здесь публичный ключ (не секрет), тот же, что в secrets/id_ed25519.pub.
# Переопределить можно в terraform.tfvars — но тогда при исключении ВМ
# надо удалить и эту строку (иного способа у Terraform нет).
default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPR8S07Mnku1VlVR/lq6hCKPo9fNzJ+7E0DoE7bkvy4p tazet@narod.ru"
description = "Публичная часть SSH-ключа в формате OpenSSH. Не изменяется после создания. По умолчанию — ключ tazet@narod.ru"
}
variable "vm_access_port_list" {
type = list(object({
port = string
type = string
}))
default = [
{ port = "22", type = "tcp" }
]
description = "Белый список портов для доступа извне; type: tcp | udp | all"
}
variable "vm_access_ip_list" {
type = list(string)
default = ["0.0.0.0/0"]
description = "Белый список адресов, которым разрешён доступ к ВМ. Требует выделенного внешнего IP"
}
variable "vm_same_snat" {
type = bool
default = false
description = "false — публикация за общим SNAT эджа; true — за выделенным внешним IP услуги"
}
# --- vApp (услуга 26) ---
resource "nubes_vapp" "vapp" {
resource_name = var.vapp_resource_name
vapp_name = var.vapp_name
vdc_uid = nubes_vc_vdc.vdc.id # ref 21 — вычислительная инфраструктура
nsxt_uid = nubes_vc_nsxt.edge.id # ref 22 — сеть/маршрутизация
# «Заморозка»: destroy переводит vApp в suspend (delete требует suspend).
suspend_on_destroy = true
# Повторный apply усыновляет существующий vApp, а не падает с
# «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ».
adopt_existing_on_create = true
# vApp требует готовую сеть (ipSpace на организации + SNAT на эдже).
depends_on = [nubes_vc_nsxt_snat.snat]
}
# --- ВМ (услуга 28) ---
resource "nubes_vc_vm_v3" "vm" {
resource_name = var.vm_resource_name
vm_name = var.vm_name
vapp_uid = nubes_vapp.vapp.id # ref 26 — ВМ размещается в vApp
image_vm = var.vm_image
vm_cpu = var.vm_cpu
vm_ram = var.vm_ram
vm_disk = var.vm_disk
user_login = var.vm_user_login
user_public_key = var.vm_user_public_key
# Внешний доступ
ip_space_name = var.ip_space_name # тот же ipSpace, что у SNAT эджа
same_snat = var.vm_same_snat
access_port_list = jsonencode(var.vm_access_port_list)
access_ip_list = jsonencode(var.vm_access_ip_list)
# «Заморозка»: destroy переводит ВМ в suspend.
suspend_on_destroy = true
adopt_existing_on_create = true
# ВМ создаётся платформой долго — поднимаем таймаут ожидания.
operation_timeout = "15m"
}
# --- Выводы ---
output "vapp_id" {
description = "UID созданного vApp (услуга 26)"
value = nubes_vapp.vapp.id
}
output "vapp_name" {
description = "Имя vApp"
value = nubes_vapp.vapp.vapp_name
}
output "vm_id" {
description = "UID созданной ВМ (услуга 28)"
value = nubes_vc_vm_v3.vm.id
}
output "vm_state_flat" {
description = "Плоский state ВМ — IP-адреса, статус и т.д."
value = nubes_vc_vm_v3.vm.state_out_flat
}