116 lines
4.1 KiB
Go
116 lines
4.1 KiB
Go
// service_spec_gen — генератор YAML-спеков сервисов из API Nubes.
|
||
//
|
||
// Читает список сервисов, дёргает API, собирает операции/параметры/MAN,
|
||
// пишет один YAML на сервис в resources_yaml/.
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"gopkg.in/yaml.v3"
|
||
|
||
"yaml-generator/internal/client"
|
||
"yaml-generator/internal/config"
|
||
"yaml-generator/internal/normalize"
|
||
"yaml-generator/internal/spec"
|
||
"yaml-generator/internal/types"
|
||
)
|
||
|
||
func main() {
|
||
cfg, err := config.Load()
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
cli := client.New(cfg.APIEndpoint, cfg.APIToken)
|
||
|
||
services := cfg.Services
|
||
if cfg.SingleServiceID > 0 {
|
||
services = []config.ServiceRef{{ID: cfg.SingleServiceID, Name: cfg.SingleServiceName}}
|
||
}
|
||
|
||
outputDir := cfg.OutputDir
|
||
if outputDir == "" {
|
||
panic("NUBES_OUTPUT_DIR is required — generated files must go to generated/{stand}/resources_yaml/")
|
||
}
|
||
if err := spec.ValidateOutputDir(outputDir); err != nil {
|
||
panic(err)
|
||
}
|
||
if err := os.MkdirAll(outputDir, 0o755); err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
for _, svc := range services {
|
||
if svc.ID <= 0 {
|
||
continue
|
||
}
|
||
fmt.Fprintf(os.Stderr, " service %d (%s): fetching...\n", svc.ID, svc.Name)
|
||
info, err := cli.GetService(svc.ID)
|
||
if err != nil {
|
||
fmt.Fprintf(os.Stderr, " ⚠ service %d (%s) skipped: %v\n", svc.ID, svc.Name, err)
|
||
continue
|
||
}
|
||
name := strings.TrimSpace(svc.Name)
|
||
if name == "" {
|
||
name = strings.TrimSpace(info.ShortName)
|
||
}
|
||
if name == "" {
|
||
name = strings.TrimSpace(info.DisplayName)
|
||
}
|
||
name = normalize.ServiceName(name)
|
||
if name == "" {
|
||
name = fmt.Sprintf("service_%d", svc.ID)
|
||
}
|
||
|
||
fmt.Fprintf(os.Stderr, " %d operations to fetch...\n", len(info.Operations))
|
||
|
||
// === Универсальная выгрузка из API (БЕЗ доменных знаний) ===
|
||
//
|
||
// CollectOperations возвращает операции сервиса ТАКИМИ, КАК ИХ ОТДАЁТ API:
|
||
// create/delete/modify/suspend/... с их настоящими действиями и параметрами.
|
||
// Здесь НЕЛЬЗЯ привносить доменную семантику (помечать операцию как
|
||
// "модификатор", задавать delete_strategy, delete_params и т.п.) — это
|
||
// сломало бы главный принцип: YAML должен быть ЧИСТОЙ, ПОЛНОЙ выгрузкой
|
||
// того, что доступно по API для сервиса, без каких-либо знаний о слое
|
||
// "модификаторов" провайдера.
|
||
//
|
||
// Раньше (до рефакторинга 2026-09-23) здесь стоял реестр
|
||
// `serviceSpecificModifiers`, который помечал отдельные modify как
|
||
// kind=modifier. Это было инвазивно: обычные ресурсы зависели от
|
||
// доменного реестра, а YAML переставал быть чистой выгрузкой.
|
||
// Модификаторы теперь живут ОТДЕЛЬНЫМ оверлеем (см. modifiers.yaml +
|
||
// второй проход), базовый YAML их не содержит и не знает о них.
|
||
ops, hasSuspend, _, _, err := cli.CollectOperations(info.Operations)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
specYAML := types.ServiceSpec{
|
||
Name: name,
|
||
ServiceID: svc.ID,
|
||
ServiceDisplayName: strings.TrimSpace(info.DisplayName),
|
||
ServiceShortName: strings.TrimSpace(info.ShortName),
|
||
ServiceMan: strings.TrimSpace(info.Man),
|
||
Lifecycle: types.Lifecycle{
|
||
SuspendOnDestroyDefault: hasSuspend,
|
||
AdoptExistingOnCreateDefault: false,
|
||
},
|
||
Outputs: types.OutputSection{Params: spec.DefaultOutputParams()},
|
||
Operations: ops,
|
||
}
|
||
|
||
outPath := filepath.Join(outputDir, fmt.Sprintf("%d_%s.yaml", svc.ID, name))
|
||
buf, err := yaml.Marshal(specYAML)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
if err := os.WriteFile(outPath, buf, 0o644); err != nil {
|
||
panic(err)
|
||
}
|
||
fmt.Printf("written %s\n", outPath)
|
||
}
|
||
}
|