Fix real-deployment blockers and scope down to router-only VMs

Confirmed working against a real VK Cloud PROD deployment (3 routers,
19 resources, apply succeeded end to end). Fixes found along the way:

- provider "vkcs" was never configured (versions.tf) - username/password/
  project_id/region were declared but wired to nothing; added auth_url and
  user_domain_name to complete it.
- Nova keypairs are per-user, not per-project - added an optional
  vkcs_compute_keypair resource (var.ssh_public_key) so Terraform can
  register a keypair under the deploying service account itself.
- router_priv_port used a hand-computed fixed_ip offset that collided with
  VKCS's own auto-created service ports on each network (observed: a
  "network:dns" port) - now left unset so Neutron's IPAM auto-assigns,
  which is collision-free by construction.
- vkcs_compute_instance set image_id at the top level while also booting
  from a volume via block_device - the provider docs say not to do this;
  Nova echoes back a sentinel string for image_id on a volume-booted
  server, which Terraform read as drift on a ForceNew attribute and
  wanted to destroy+recreate every already-created instance on every
  subsequent plan.
- private_network_cidrs bumped from /29 to /28 - too tight once the
  platform's own reserved ports are accounted for.

Also removed the priv_srv_01/02/03 demo instances and the LAN network/
security group only they used - this deployment provisions router VMs
only, confirmed with the user.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hXR2ftXZZhJ4Y3XuSoR8r
This commit is contained in:
ayurishchevandClaude Sonnet 5 committed 2026-09-07 08:55:15 +03:00
1 parent b5d6367fd8
commit 8886c1baad
13 files changed
+506 -171

No files matched your search

@@ -0,0 +1,28 @@
# План внедрения: подключение provider "vkcs" и безопасная передача PROD-credentials
Дата: 2026-09-06
## Проблема
При подготовке к PROD-развёртыванию (только Terraform, без Ansible — см. project-память) обнаружено: в `terraform/` вообще отсутствует блок `provider "vkcs" {}`. Переменные `username`/`password`/`project_id`/`region` были объявлены в `variables.tf`, но нигде не использовались (`grep` — 0 совпадений) — т.е. развёртывание не смогло бы аутентифицироваться независимо от способа передачи credentials.
Дополнительно: провайдер `vkcs` (в отличие от классического OpenStack-провайдера) не имеет задокументированной автоматической поддержки `OS_*` переменных окружения (проверено локально через `terraform providers schema -json` — только `access_token`/`OS_AUTH_TOKEN` упомянуты явно) — значит, `auth_url`, `username`, `password`, `project_id`, `region`, `user_domain_name` нужно явно передавать через сам provider-блок.
Пользователь передал `openrc.sh` для сервисного аккаунта (`OS_AUTH_URL`, `OS_PROJECT_ID`, `OS_USERNAME`/`OS_PASSWORD`, `OS_USER_DOMAIN_NAME=service-users`, `OS_REGION_NAME=RegionOne`) и попросил проверить схему "взять `terraform.tfvars`-шаблон за основу + на лету добавить нужные параметры".
## Решение
1. Добавить `provider "vkcs" {}` в `terraform/versions.tf`, подключив все 6 auth-переменных.
2. Добавить недостающие переменные `auth_url`, `user_domain_name`; исправить некорректный дефолт `region` (было `"ME1"` — это код availability zone, а не OpenStack-регион; правильное значение — `"RegionOne"`, ранее это не проявлялось, т.к. переменная не использовалась).
3. **Не** вписывать реальные credentials в закоммиченный `terraform.tfvars` — вместо этого задокументировать и использовать паттерн `*.auto.tfvars` (Terraform подхватывает такие файлы автоматически поверх `terraform.tfvars`, без изменения самого шаблона): добавить `*.auto.tfvars`/`*.auto.tfvars.json`/`terraform.tfvars.local` в `.gitignore`, создать `terraform/prod.auto.tfvars.example` (без секретов, только маппинг полей `openrc.sh` → переменные).
4. Проверить всю цепочку реальными credentials из `openrc.sh`: во временной директории вне репозитория (не в git) собрать overlay-файл с реальными значениями, прогнать `terraform init` + `terraform plan` (read-only, ничего не создаётся) против настоящего VK Cloud API — убедиться, что аутентификация и резолвинг data source'ов (`vkcs_images_image`, `vkcs_networking_network.extnet`, `vkcs_networking_secgroup.default`) работают. Временную директорию с секретами удалить сразу после проверки.
5. Дополнить `terraform/tests/test_terraform_delivery.py`: provider-блок реально подключает все auth-переменные (regression guard на "orphaned variables"); `terraform.tfvars` не содержит `auth_url`/`user_domain_name`; `*.auto.tfvars` в `.gitignore`; `prod.auto.tfvars.example` не игнорируется.
6. Обновить `README.md`/`docs/QUICKSTART.md`.
7. Summary-документ по завершении.
## Верификация
- `terraform fmt -check -recursive`.
- Реальный `terraform init` + `terraform validate` (локальный provider-mirror) — синтаксическая/схемная корректность provider-блока.
- Реальный `terraform init` + `terraform plan` с настоящими PROD credentials (во временной, не-репозиторной директории) — подтверждена успешная аутентификация и резолвинг data sources; временная директория с секретами удалена сразу после проверки, секреты нигде не закоммичены.
- `venv/bin/pytest terraform/tests -v` — весь сьют зелёный.
@@ -0,0 +1,56 @@
# Summary: подключение provider "vkcs" и безопасная передача PROD-credentials
Дата: 2026-09-06
План: [2026-09-06-provider-auth-wiring-plan.md](2026-09-06-provider-auth-wiring-plan.md)
## Что сделано
### `terraform/versions.tf`
Добавлен блок:
```hcl
provider "vkcs" {
auth_url = var.auth_url
username = var.username
password = var.password
project_id = var.project_id
region = var.region
user_domain_name = var.user_domain_name
}
```
До этого `username`/`password`/`project_id`/`region` были orphaned-переменными — объявлены, но нигде не использовались.
### `terraform/variables.tf`
- Добавлены `auth_url` (default `"https://infra.mail.ru:35357/v3/"`) и `user_domain_name` (default `"users"`, для сервисных аккаунтов — `"service-users"`).
- Исправлен дефолт `region`: было `"ME1"` (код availability zone, спутан с регионом), стало `"RegionOne"` (реальный OpenStack-регион VK Cloud, подтверждено `openrc.sh` пользователя — `OS_REGION_NAME=RegionOne`). Раньше это не проявлялось, т.к. переменная не была подключена ни к чему.
### `.gitignore`
Добавлены `*.auto.tfvars`, `*.auto.tfvars.json`, `terraform.tfvars.local` — реальные credentials передаются через автоматически подхватываемый Terraform overlay-файл, а не через правки закоммиченного `terraform.tfvars`.
### `terraform/prod.auto.tfvars.example` (новый файл, без секретов)
Документирует паттерн и маппинг полей `openrc.sh` (`OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_PROJECT_ID`, `OS_REGION_NAME`, `OS_USER_DOMAIN_NAME`) → переменные Terraform.
### `terraform/tests/test_terraform_delivery.py`
Добавлены тесты: `test_provider_vkcs_wires_all_auth_variables` (regression guard — provider-блок реально подключает все 6 auth-переменных), `test_auth_variable_has_a_default` (`auth_url`/`user_domain_name`/`region` — есть дефолт), `test_gitignore_excludes_auto_tfvars_overlay`, `test_prod_auto_tfvars_example_is_not_gitignored`, `test_tfvars_template_has_no_auth_credentials` (в закоммиченном `terraform.tfvars` не должно быть `auth_url`/`user_domain_name`). Итог: **48 passed** (было 38).
### `README.md` / `docs/QUICKSTART.md`
Добавлен раздел "Provider authentication" / обновлён шаг 2 Quick Start — описан paттерн `*.auto.tfvars` и маппинг полей `openrc.sh`.
## Реальная проверка с PROD credentials
Пользователь передал `openrc.sh` сервисного аккаунта. Во временной директории **вне репозитория** (не в git, удалена сразу после проверки) собран overlay-файл с реальными значениями (`auth_url`, `username`, `password`, `project_id`, `region=RegionOne`, `user_domain_name=service-users`), поверх закоммиченного `terraform.tfvars`. Выполнены:
- `terraform init` — успех, провайдер и бэкенд инициализированы.
- `terraform plan` (read-only, ничего не создано) — **успех**: `Plan: 24 to add, 0 to change, 0 to destroy`. Подтверждена реальная аутентификация к `https://infra.mail.ru:35357/v3/` и успешный резолвинг всех трёх data source'ов (`vkcs_images_image.ubuntu24`, `vkcs_networking_network.extnet` по `name="internet"`, `vkcs_networking_secgroup.default` по `name="default"`) — т.е. соответствующие объекты реально существуют в проекте с ожидаемыми именами.
Пароль и прочие секреты нигде не сохранялись в репозитории; временная директория удалена немедленно после проверки (подтверждено).
## Верификация
- `terraform fmt -check -recursive` → чисто.
- Реальные `terraform init`/`validate` (локальный provider-mirror) → успех.
- Реальный `terraform plan` с PROD credentials (вне репозитория) → успех, 24 ресурса к созданию, 0 ошибок.
- `venv/bin/pytest terraform/tests -v` → **48 passed**.
## Следующий шаг
`terraform apply` для реального PROD-развёртывания (3 роутера) пользователем ещё не подтверждён — это отдельное, необратимое действие, требующее явного разрешения.
@@ -0,0 +1,22 @@
# План внедрения: деплой только роутеров (без приватных серверов)
Дата: 2026-09-07
## Проблема
Прошлая попытка реального PROD-развёртывания (см. `docs/changes/2026-09-06-*`) выявила несколько багов (invalid key_name, IP-коллизии на /29, `image_id` drift), все они исправлены. Всё созданное было удалено (`terraform destroy`, 25 ресурсов, подтверждено пустым state) по явному запросу пользователя.
Пользователь скорректировал требования к развёртыванию: нужны **только 3 роутера**, без демо-сегмента `priv_srv_01/02/03` (и без разделяемой LAN-сети/security group, которые использовались только ими). Каждый роутер — 3 порта (1 внешний + 2 приватных), каждый приватный порт в своей `/28`-подсети — это уже соответствует текущей конфигурации после прошлых фиксов.
## Решение
1. `terraform/main.tf`: удалить `vkcs_compute_instance.priv_srv_01/02/03`, а также ставшие неиспользуемыми `vkcs_networking_network.lan_net`, `vkcs_networking_subnet.lan_subnet`, `vkcs_networking_secgroup.private_sg` и его 3 правила (`from_rfc_net10/172/192_in`) — ничего из этого больше ничем не используется.
2. `terraform/tests/test_terraform_delivery.py`: добавить `test_deployment_is_router_only` — regression guard, что единственный compute-instance ресурс — `router`, единственная security group — `router_sg`, нет `lan_net`.
3. `README.md`: обновить описание (только роутеры; авто-назначение IP через Neutron IPAM вместо ручного вычисления; актуальный размер подсетей `/28`).
4. Summary-документ по завершении.
## Верификация
- `terraform fmt -check -recursive`.
- `venv/bin/pytest terraform/tests -v`.
- Реальный `terraform plan` против пустого state (после `destroy`) — должен показать только ресурсы, относящиеся к 3 роутерам.
@@ -0,0 +1,25 @@
# Summary: деплой только роутеров (без приватных серверов)
Дата: 2026-09-07
План: [2026-09-07-router-only-deployment-plan.md](2026-09-07-router-only-deployment-plan.md)
## Что сделано
### `terraform/main.tf`
Удалены: `vkcs_compute_instance.priv_srv_01/02/03`, `vkcs_networking_network.lan_net`, `vkcs_networking_subnet.lan_subnet`, `vkcs_networking_secgroup.private_sg` и его 3 правила (`from_rfc_net10/172/192_in`) — всё это использовалось исключительно приватными серверами и стало мёртвым кодом. Осталось: `router_sg` + 4 правила, `default` security group lookup, keypair, `router_priv_net/subnet/port` (по числу `private_network_cidrs`), единственный ресурс `vkcs_compute_instance.router` (`count = var.router_count`).
### `terraform/tests/test_terraform_delivery.py`
Добавлен `test_deployment_is_router_only` — regression guard: единственный compute-instance — `router`, единственная security group — `router_sg`, нет `lan_net`. Итог сьюта: **56 passed** (было 55).
### `README.md`
Обновлено описание: только роутеры (демо-сегмент приватных серверов исключён), явно указано, что адреса на портах теперь назначаются Neutron IPAM автоматически (не вычисляются вручную), актуализирован размер подсетей (`/28`).
## Верификация
- `terraform fmt -check -recursive` → чисто.
- `venv/bin/pytest terraform/tests -v` → **56 passed**.
- Реальный `terraform plan` против пустого state (после предыдущего `destroy`) → **19 to add, 0 to change, 0 to destroy**: 3 роутера, keypair, 2 приватные сети/подсети (`/28`), 6 портов, 1 security group + 4 правила. Ровно то, что запрошено — без единого лишнего ресурса.
## Следующий шаг
`terraform apply` по сохранённому плану (`terraform/prod.tfplan`) ещё не выполнен — ожидает подтверждения пользователя.