docs: план AI-анализа terraform plan (уровни 0-5, Google Gemini → Cloud LLM)
This commit is contained in:
@@ -0,0 +1,269 @@
|
|||||||
|
# План: AI-анализ terraform plan (уровни 0–5)
|
||||||
|
|
||||||
|
Дата: 2026-03-12
|
||||||
|
Статус: черновик / на согласование
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Суть задачи
|
||||||
|
|
||||||
|
После выполнения `terraform plan` отправлять вывод на анализ в LLM.
|
||||||
|
Глубина анализа управляется переменной `ai_hint_level` (0–5) в Terraform-конфигурации.
|
||||||
|
Сейчас — Google AI (Gemini API). В будущем — собственный облачный LLM (drop-in замена).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Уровни подсказок (`ai_hint_level`)
|
||||||
|
|
||||||
|
| Уровень | Название | Что делает |
|
||||||
|
|---------|----------------|-------------------------------------------------------------------|
|
||||||
|
| 0 | OFF | Ничего. AI не вызывается. `terraform plan` работает как обычно. |
|
||||||
|
| 1 | SYNTAX | Проверка синтаксиса: ошибки в HCL, опечатки, невалидные блоки. |
|
||||||
|
| 2 | BASIC | + базовый анализ: неиспользуемые ресурсы, пустые значения. |
|
||||||
|
| 3 | MODERATE | + анализ зависимостей, потенциальные конфликты, порядок apply. |
|
||||||
|
| 4 | DETAILED | + best practices, безопасность (открытые порты, широкие IAM). |
|
||||||
|
| 5 | FULL | Полные рекомендации: оптимизация, рефакторинг, альтернативы. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Как задаётся уровень
|
||||||
|
|
||||||
|
**По умолчанию AI не активен и ничего не показывает.** Если `ai_hint_level` нигде не задан — уровень 0, `sless plan` работает как обычный `terraform plan`, без единого упоминания об AI.
|
||||||
|
|
||||||
|
Уровень задаётся **только явно**, одним из способов (приоритет сверху вниз):
|
||||||
|
|
||||||
|
1. Env-переменная (рекомендуется):
|
||||||
|
```bash
|
||||||
|
export TF_VAR_ai_hint_level=3
|
||||||
|
```
|
||||||
|
|
||||||
|
2. В `variables.tf` пользовательского проекта (опционально, только если нужно зафиксировать уровень в коде):
|
||||||
|
```hcl
|
||||||
|
variable "ai_hint_level" {
|
||||||
|
description = "Уровень AI-анализа terraform plan (0=off, 5=full)"
|
||||||
|
type = number
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = var.ai_hint_level >= 0 && var.ai_hint_level <= 5
|
||||||
|
error_message = "ai_hint_level должен быть от 0 до 5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. CLI-флаг напрямую:
|
||||||
|
```bash
|
||||||
|
sless plan --ai-level=3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Важно:** CLI сам читает уровень из env `TF_VAR_ai_hint_level` или флага `--ai-level`. Если ни того ни другого нет — молча применяет уровень 0. Никакой переменной в манифестах не требуется. Пользователь не видит AI, пока не включит его явно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Архитектура решения
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||||
|
│ terraform │────>│ wrapper-скрипт │────>│ AI Analyzer │
|
||||||
|
│ plan │ │ (bash/Go CLI) │ │ (Go сервис) │
|
||||||
|
│ (вывод JSON)│ │ │ │ │
|
||||||
|
└─────────────┘ └──────────────────┘ └────────┬────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ LLM Backend │
|
||||||
|
│ (Google Gemini) │
|
||||||
|
│ → Cloud LLM │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Компоненты:
|
||||||
|
|
||||||
|
1. **Wrapper-скрипт / CLI** — вызывает `terraform plan -json`, читает `ai_hint_level`, передаёт вывод в анализатор.
|
||||||
|
2. **AI Analyzer (Go)** — формирует промпт по уровню, отправляет в LLM, форматирует ответ.
|
||||||
|
3. **LLM Backend** — подключаемый через интерфейс (сначала Google, потом облачный).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Реализация по шагам
|
||||||
|
|
||||||
|
### Шаг 1: CLI-обёртка (`sless-plan`)
|
||||||
|
|
||||||
|
Новый бинарник или shell-скрипт, который:
|
||||||
|
- Запускает `terraform plan -json -out=plan.bin`
|
||||||
|
- Читает `ai_hint_level` из state/variables (через `terraform output` или парсинг .tf)
|
||||||
|
- Если level > 0, передаёт JSON-вывод плана в AI Analyzer
|
||||||
|
- Выводит стандартный plan + AI-рекомендации ниже
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Пользователь вызывает:
|
||||||
|
sless plan # вместо terraform plan
|
||||||
|
# или:
|
||||||
|
terraform plan && sless analyze --level=3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2: AI Analyzer сервис (Go)
|
||||||
|
|
||||||
|
Расположение: `internal/ai/` или отдельный `cmd/ai-analyzer/`
|
||||||
|
|
||||||
|
```
|
||||||
|
internal/ai/
|
||||||
|
├── analyzer.go # основная логика: формирование промпта, парсинг ответа
|
||||||
|
├── provider.go # интерфейс LLM-провайдера
|
||||||
|
├── google_gemini.go # реализация для Google Gemini API
|
||||||
|
├── cloud_llm.go # (заглушка) реализация для будущего облачного LLM
|
||||||
|
└── prompts.go # шаблоны промптов по уровням 1–5
|
||||||
|
```
|
||||||
|
|
||||||
|
Ключевой интерфейс:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// LLMProvider — абстракция над любым LLM-бэкендом.
|
||||||
|
// Сейчас: Google Gemini. В будущем: облачный LLM (drop-in замена).
|
||||||
|
type LLMProvider interface {
|
||||||
|
Analyze(ctx context.Context, prompt string) (string, error)
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3: Промпты по уровням
|
||||||
|
|
||||||
|
Каждый уровень = свой system prompt + ограничения:
|
||||||
|
|
||||||
|
| Уровень | System prompt (суть) |
|
||||||
|
|---------|-----------------------------------------------------------|
|
||||||
|
| 1 | "Проверь только синтаксис HCL. Ничего лишнего." |
|
||||||
|
| 2 | "Синтаксис + базовые проблемы (unused, empty values)." |
|
||||||
|
| 3 | "Анализ зависимостей, порядок, потенциальные конфликты." |
|
||||||
|
| 4 | "Best practices, безопасность, IAM, открытые порты." |
|
||||||
|
| 5 | "Полный аудит: оптимизация, рефакторинг, альтернативы." |
|
||||||
|
|
||||||
|
### Шаг 4: Google Gemini интеграция
|
||||||
|
|
||||||
|
- API: `generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent`
|
||||||
|
- Авторизация: API key (хранить в env `GOOGLE_AI_API_KEY`, не в коде)
|
||||||
|
- SDK: `github.com/google/generative-ai-go` (официальный Go SDK)
|
||||||
|
- Модель: `gemini-pro` или `gemini-1.5-pro`
|
||||||
|
|
||||||
|
```go
|
||||||
|
// google_gemini.go — реализует LLMProvider
|
||||||
|
type GeminiProvider struct {
|
||||||
|
client *genai.Client
|
||||||
|
model string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 5: Конфигурация
|
||||||
|
|
||||||
|
Через env-переменные (не хардкод):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Какой LLM-провайдер использовать
|
||||||
|
export AI_PROVIDER=google # google | cloud (в будущем)
|
||||||
|
|
||||||
|
# Google Gemini
|
||||||
|
export GOOGLE_AI_API_KEY=AIza... # API ключ
|
||||||
|
|
||||||
|
# Будущий облачный LLM
|
||||||
|
export CLOUD_LLM_ENDPOINT=https://llm.cloud.example.com/v1/analyze
|
||||||
|
export CLOUD_LLM_TOKEN=...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Миграция на облачный LLM
|
||||||
|
|
||||||
|
Когда облако предоставит свой LLM:
|
||||||
|
|
||||||
|
1. Создать `cloud_llm.go` — реализация `LLMProvider` для нового API
|
||||||
|
2. Переключить `AI_PROVIDER=cloud` в env
|
||||||
|
3. Код анализатора НЕ меняется — интерфейс `LLMProvider` тот же
|
||||||
|
4. Google Gemini остаётся как fallback (опционально)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Фабрика провайдеров — выбор по переменной окружения
|
||||||
|
func NewProvider(cfg Config) (LLMProvider, error) {
|
||||||
|
switch cfg.Provider {
|
||||||
|
case "google":
|
||||||
|
return NewGeminiProvider(cfg.GoogleAPIKey)
|
||||||
|
case "cloud":
|
||||||
|
return NewCloudLLMProvider(cfg.CloudEndpoint, cfg.CloudToken)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown AI provider: %s", cfg.Provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Безопасность
|
||||||
|
|
||||||
|
- API-ключи — только в env/secrets, никогда в коде или .tf файлах
|
||||||
|
- `terraform plan -json` может содержать sensitive data → фильтровать перед отправкой в LLM
|
||||||
|
- Ограничить размер payload (большие планы обрезать/суммаризировать)
|
||||||
|
- Логировать факт вызова AI, но НЕ содержимое запроса/ответа в production
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Структура файлов (итого)
|
||||||
|
|
||||||
|
```
|
||||||
|
sless/
|
||||||
|
├── cmd/
|
||||||
|
│ └── sless-plan/ # CLI-обёртка для terraform plan + AI
|
||||||
|
│ └── main.go
|
||||||
|
├── internal/
|
||||||
|
│ └── ai/
|
||||||
|
│ ├── analyzer.go # логика анализа
|
||||||
|
│ ├── provider.go # интерфейс LLMProvider
|
||||||
|
│ ├── google_gemini.go # Google Gemini реализация
|
||||||
|
│ ├── cloud_llm.go # заглушка для облачного LLM
|
||||||
|
│ └── prompts.go # промпты по уровням 1–5
|
||||||
|
├── terraform/
|
||||||
|
│ └── provider/ # (существующий провайдер — без изменений)
|
||||||
|
└── examples/
|
||||||
|
└── */
|
||||||
|
└── variables.tf # добавить ai_hint_level variable
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Порядок реализации
|
||||||
|
|
||||||
|
| # | Задача | Приоритет |
|
||||||
|
|----|------------------------------------------------|-----------|
|
||||||
|
| 1 | Создать `internal/ai/provider.go` (интерфейс) | HIGH |
|
||||||
|
| 2 | Реализовать `google_gemini.go` | HIGH |
|
||||||
|
| 3 | Написать промпты по уровням (`prompts.go`) | HIGH |
|
||||||
|
| 4 | Создать `analyzer.go` (оркестрация) | HIGH |
|
||||||
|
| 5 | CLI-обёртка `cmd/sless-plan/main.go` | HIGH |
|
||||||
|
| 6 | Добавить `ai_hint_level` в examples | MEDIUM |
|
||||||
|
| 7 | Тесты (unit + integration с mock LLM) | MEDIUM |
|
||||||
|
| 8 | Заглушка `cloud_llm.go` для будущей миграции | LOW |
|
||||||
|
| 9 | Документация для пользователей | MEDIUM |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Пример использования (целевой UX)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# В terraform проекте пользователя:
|
||||||
|
$ export TF_VAR_ai_hint_level=3
|
||||||
|
$ export GOOGLE_AI_API_KEY=AIza...
|
||||||
|
|
||||||
|
$ sless plan
|
||||||
|
# Terraform Plan output (стандартный)
|
||||||
|
# ...
|
||||||
|
# ──────────────── AI Analysis (level 3: MODERATE) ────────────────
|
||||||
|
# ⚠ Resource "kubernetes_deployment.func" зависит от "kubernetes_namespace.ns"
|
||||||
|
# но namespace создаётся в другом модуле — возможен race condition.
|
||||||
|
# ⚠ Порядок destroy может вызвать проблемы: trigger удалится раньше function.
|
||||||
|
# ✓ Синтаксис корректен, конфликтов не обнаружено.
|
||||||
|
# ─────────────────────────────────────────────────────────────────
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Решение зафиксировано
|
||||||
|
|
||||||
|
Подход: **интерфейс LLMProvider** → сначала Google Gemini → потом drop-in замена на облачный LLM.
|
||||||
|
Уровень задаётся через обычную Terraform variable. Никаких изменений в самом провайдере sless.
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Инструкции: монтирование удалённой папки через SSHFS
|
||||||
|
|
||||||
|
Дата: 2026-03-12
|
||||||
|
|
||||||
|
Кратко: эти команды повторяют текущее у вас подключение, где
|
||||||
|
источник: `naeel@5.172.178.213:/home/naeel/terra`, и точка монтирования — `/home/<user>/remote_dev`.
|
||||||
|
|
||||||
|
1) Установка `sshfs` (Debian/Ubuntu):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y sshfs
|
||||||
|
```
|
||||||
|
|
||||||
|
2) Подготовка приватного ключа:
|
||||||
|
|
||||||
|
- Скопируйте приватный ключ на целевую машину или загрузите его безопасным способом.
|
||||||
|
- Пример копирования с локальной машины на целевой хост:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp /home/naeel/remote_dev/sless/secrets/naeel_vm_id_ed25519 user@target:/home/user/.ssh/id_ed25519_sless
|
||||||
|
ssh user@target 'chmod 600 /home/user/.ssh/id_ed25519_sless'
|
||||||
|
```
|
||||||
|
|
||||||
|
(Оригинал ключа в репозитории: `secrets/naeel_vm_id_ed25519`)
|
||||||
|
|
||||||
|
3) Создать точку монтирования на целевом хосте:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /home/user/remote_dev
|
||||||
|
chown user:user /home/user/remote_dev
|
||||||
|
```
|
||||||
|
|
||||||
|
4) Команда монтирования (пример, повторяет ваше текущее подключение):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sshfs -o IdentityFile=/home/user/.ssh/id_ed25519_sless \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
-o UserKnownHostsFile=/dev/null \
|
||||||
|
-o reconnect \
|
||||||
|
-o ServerAliveInterval=15 \
|
||||||
|
-o ServerAliveCountMax=3 \
|
||||||
|
naeel@5.172.178.213:/home/naeel/terra /home/user/remote_dev
|
||||||
|
```
|
||||||
|
|
||||||
|
5) Проверка статуса монтирования и содержимого:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
findmnt /home/user/remote_dev
|
||||||
|
ls -la /home/user/remote_dev
|
||||||
|
```
|
||||||
|
|
||||||
|
6) Отключение:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fusermount -u /home/user/remote_dev
|
||||||
|
```
|
||||||
|
|
||||||
|
7) (Опционально) systemd unit для автоподключения при старте:
|
||||||
|
|
||||||
|
Создайте файл `/etc/systemd/system/remote_dev.mount` со следующим содержимым (отредактируйте путь к ключу и `Where`):
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=SSHFS mount for /home/user/remote_dev
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Mount]
|
||||||
|
What=naeel@5.172.178.213:/home/naeel/terra
|
||||||
|
Where=/home/user/remote_dev
|
||||||
|
Type=fuse.sshfs
|
||||||
|
Options=IdentityFile=/home/user/.ssh/id_ed25519_sless,allow_other,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,StrictHostKeyChecking=no,UserKnownHostsFile=/dev/null
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Затем выполнить:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now remote_dev.mount
|
||||||
|
```
|
||||||
|
|
||||||
|
8) Заметки по безопасности и надёжности:
|
||||||
|
|
||||||
|
- Храните приватный ключ с правами `600` и ограниченным доступом.
|
||||||
|
- Рассмотрите использование `ssh-agent` вместо копирования ключа.
|
||||||
|
- Опция `StrictHostKeyChecking=no` ослабляет проверку host key — используйте осознанно.
|
||||||
|
- Для массовой настройки можете создать скрипт установки и unit-файл через Ansible/Cloud-init.
|
||||||
|
|
||||||
|
---
|
||||||
|
Файл создан автоматически по запросу пользователя. Если хотите, могу:
|
||||||
|
- добавить инструкции для `macOS` или `CentOS`/`RHEL`;
|
||||||
|
- создать такой же unit-файл и загрузить его на целевой хост (при доступе по SSH).
|
||||||
Reference in New Issue
Block a user