feat(tofu): OpenTofu provisioning scaffold, auth modes and pilot notes
- providers.tf / variables.tf / versions.tf: bpg/proxmox ~> 0.84, endpoint and credentials from TF_VAR_* (set by the Makefile tofu-* targets from the repo-root .env). Two auth modes: root@pam by password (privileged: features beyond nesting, device passthrough, datastore mount points) or the ansible@pve token. - README.md: pilot results on VMID 199 - what the token can and cannot do, why a root token still fails the literal `$authuser eq 'root@pam'` check, the cmode/console drift finding, and the chosen root@pam-by-password mode. - pilot.tf.example: reference resource shape (features, device_passthrough, mount_point), not loaded (.example). - .terraform.lock.hcl: pin the provider. State has no backend yet; tofu/*.tfstate stays local and git-ignored. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uoq5AVK8mkBgg83Mq6o5V
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e0c53a1b1b
commit
646f2bbc8f
@@ -0,0 +1,134 @@
|
||||
# ============================================================================
|
||||
# ОБРАЗЕЦ, НЕ ЗАГРУЖАЕТСЯ. Расширение .example выбрано намеренно: сам пилот
|
||||
# (VMID 199) снесён 2026-09-02, и будь этот файл активным, каждый `tofu-plan`
|
||||
# предлагал бы создать его заново.
|
||||
#
|
||||
# Держим как рабочий пример формы ресурса: по нему описываются реальные
|
||||
# сервисы при переезде. См. docs/ai/migration-tofu.md, шаг 4.2.
|
||||
# ============================================================================
|
||||
#
|
||||
# Пилот: одноразовый LXC, созданный OpenTofu.
|
||||
# ============================================================================
|
||||
#
|
||||
# ЗАЧЕМ
|
||||
# Проверить на живом кластере ровно те места, из-за которых переезд на Tofu
|
||||
# выглядел рискованным, не трогая ни один боевой контейнер:
|
||||
#
|
||||
# 1. Проброс /dev/fuse. В pve-*.yml он сделан правкой /etc/pve/lxc/<vmid>.conf
|
||||
# (lxc.cgroup2.devices.allow + lxc.mount.entry), потому что Proxmox API
|
||||
# сырые lxc.* ключи не принимает. У провайдера для этого есть
|
||||
# features.fuse — штатный флаг PVE, а не обход.
|
||||
# 2. Проброс /dev/net/tun — блок device_passthrough (dev0: в PVE 8.2+).
|
||||
# 3. Mount point на storage вместо bind mount каталога хоста. Именно bind
|
||||
# mount у gitea (mp0: /opt/data/gitea) требует прав root@pam и не
|
||||
# создаётся API-токеном; volume на datastore — создаётся.
|
||||
#
|
||||
# Контейнер намеренно не входит в inventory и не автозапускается.
|
||||
# Снести после проверки: make tofu-destroy CONFIRM=1
|
||||
#
|
||||
# VMID 199 и 192.168.1.39 выбраны свободными на 2026-09-02 и лежат вне
|
||||
# диапазона сервисов, но внутри маршрутов ru-vps (192.168.1.5-40).
|
||||
|
||||
locals {
|
||||
# Привилегированный режим = аутентификация root@pam по паролю. Makefile
|
||||
# выбирает его, когда в корневом .env задан PROXMOX_ROOT_PASSWORD.
|
||||
privileged = var.pve_password != ""
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_container" "pilot" {
|
||||
node_name = "cloud-pc"
|
||||
vm_id = 199
|
||||
unprivileged = true
|
||||
start_on_boot = false
|
||||
started = true
|
||||
tags = ["tofu", "pilot"]
|
||||
|
||||
initialization {
|
||||
hostname = "tofu-pilot"
|
||||
|
||||
ip_config {
|
||||
ipv4 {
|
||||
address = "192.168.1.39/24"
|
||||
gateway = "192.168.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
dns {
|
||||
servers = ["1.1.1.1"]
|
||||
}
|
||||
|
||||
user_account {
|
||||
keys = [trimspace(file(pathexpand("~/.ssh/id_ed25519_homelab.pub")))]
|
||||
}
|
||||
}
|
||||
|
||||
operating_system {
|
||||
template_file_id = "local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst"
|
||||
type = "debian"
|
||||
}
|
||||
|
||||
cpu {
|
||||
cores = 1
|
||||
}
|
||||
|
||||
memory {
|
||||
dedicated = 512
|
||||
swap = 512
|
||||
}
|
||||
|
||||
disk {
|
||||
datastore_id = "data"
|
||||
size = 8
|
||||
}
|
||||
|
||||
# Проверка №1 и №2: то, что в pve-*.yml делается правкой конфига на узле.
|
||||
# Токену Proxmox разрешает только nesting; keyctl и fuse требуют root@pam.
|
||||
# null означает «не задавать поле», а не «выключить».
|
||||
features {
|
||||
nesting = true
|
||||
keyctl = local.privileged ? true : null
|
||||
fuse = local.privileged ? true : null
|
||||
}
|
||||
|
||||
# Проброс устройства Proxmox разрешает только root@pam (403 для токена,
|
||||
# проверено на пилоте 2026-09-02), поэтому блок появляется лишь в
|
||||
# привилегированном режиме.
|
||||
dynamic "device_passthrough" {
|
||||
for_each = local.privileged ? [1] : []
|
||||
content {
|
||||
path = "/dev/net/tun"
|
||||
}
|
||||
}
|
||||
|
||||
# Проверка №3: volume на datastore, а не bind mount каталога хоста.
|
||||
mount_point {
|
||||
volume = "data"
|
||||
size = "4G"
|
||||
path = "/opt/pilot-data"
|
||||
}
|
||||
|
||||
network_interface {
|
||||
name = "eth0"
|
||||
bridge = "vmbr0"
|
||||
firewall = true
|
||||
}
|
||||
|
||||
startup {
|
||||
order = 999
|
||||
}
|
||||
|
||||
# ignore_changes = [features] здесь НЕТ намеренно. Он нужен только в
|
||||
# гибридной схеме, где features выставляет Ansible через `pct set`: иначе
|
||||
# Tofu на следующем apply откатывает их и ломает Docker в контейнере
|
||||
# (проверено на пилоте). В привилегированном режиме features описаны выше
|
||||
# декларативно, и обход не требуется.
|
||||
}
|
||||
|
||||
output "pilot" {
|
||||
description = "Что проверять на узле после apply"
|
||||
value = {
|
||||
vmid = proxmox_virtual_environment_container.pilot.vm_id
|
||||
node = proxmox_virtual_environment_container.pilot.node_name
|
||||
verify = "ssh cloud-pc sudo pct config ${proxmox_virtual_environment_container.pilot.vm_id}"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user