diff --git a/TOOLS/docs-generator/internal/types/types.go b/TOOLS/docs-generator/internal/types/types.go new file mode 100644 index 0000000..447119c --- /dev/null +++ b/TOOLS/docs-generator/internal/types/types.go @@ -0,0 +1,71 @@ +// Package types — структуры данных для генератора документации. +package types + +// ParamSpec — параметр операции. +type ParamSpec struct { + ID int `yaml:"id"` + Code string `yaml:"code"` + DataType string `yaml:"data_type"` + Type string `yaml:"type"` + Required bool `yaml:"required"` + Default interface{} `yaml:"default"` + Descr string `yaml:"descr"` + Man string `yaml:"man"` + RefSvcID *int `yaml:"ref_svc_id"` + Func string `yaml:"func"` + MinValue *int `yaml:"minvalue"` + MaxValue *int `yaml:"maxvalue"` + Regex string `yaml:"regex"` + ValueList []string `yaml:"value_list"` + Unique string `yaml:"unique_scope"` + MaxLength *int `yaml:"maxlength"` + MinLength *int `yaml:"minlength"` +} + +// OperationSpec — операция сервиса. +type OperationSpec struct { + Name string `yaml:"name"` + ID int `yaml:"id"` + Kind string `yaml:"kind"` + Action string `yaml:"action"` + Subresource string `yaml:"subresource"` + Man string `yaml:"man"` + Params []ParamSpec `yaml:"params"` +} + +// OutputParam — выходной параметр. +type OutputParam struct { + Code string `yaml:"code"` + Type string `yaml:"type"` + Sensitive bool `yaml:"sensitive"` +} + +// ServiceSpec — YAML-спек сервиса. +type ServiceSpec struct { + Name string `yaml:"name"` + ServiceID int `yaml:"service_id"` + ServiceDisplayName string `yaml:"service_display_name"` + ServiceMan string `yaml:"service_man"` + Lifecycle struct { + SuspendOnDestroyDefault bool `yaml:"suspend_on_destroy_default"` + AdoptExistingOnCreateDefault bool `yaml:"adopt_existing_on_create_default"` + } `yaml:"lifecycle"` + Outputs struct { + Params []OutputParam `yaml:"params"` + } `yaml:"outputs"` + Operations []OperationSpec `yaml:"operations"` +} + +// ServiceMeta — запись из services_list.txt. +type ServiceMeta struct { + ID int + Name string +} + +// CloudOutputSnapshot — снимок выходных полей из облака. +type CloudOutputSnapshot struct { + ServiceID int `json:"serviceId"` + ServiceName string `json:"serviceName"` + OutPaths []string `json:"out_paths"` + VaultFields []string `json:"vault_fields"` +} diff --git a/TOOLS/docs-generator/internal/writers/writers.go b/TOOLS/docs-generator/internal/writers/writers.go new file mode 100644 index 0000000..feaba90 --- /dev/null +++ b/TOOLS/docs-generator/internal/writers/writers.go @@ -0,0 +1,859 @@ +// Package writers — генерация Markdown-документации для ресурсов. +package writers + +import ( + "bytes" + "encoding/json" + "fmt" + "html" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + + "docs-generator/internal/types" +) + +// CloudOutputByServiceID глобальный кэш снепшотов облачных выходов. +var CloudOutputByServiceID = map[int]types.CloudOutputSnapshot{} + +// ResourceDocs генерирует полный набор .md файлов для одного сервиса. +func ResourceDocs(docsDir string, spec types.ServiceSpec, version string, apiEndpoint string) { + base := spec.Name + nav := fmt.Sprintf("[Manual](%s.md) | [Create params](%s_params_create.md) | [Modify params](%s_params_modify.md) | [Output params](%s_outputs.md) | [Operations](%s_ops.md) | [Example](%s_example.md)", base, base, base, base, base, base) + + WriteFile(filepath.Join(docsDir, base+".md"), buildManualPage(spec, nav)) + WriteFile(filepath.Join(docsDir, base+"_example.md"), buildExamplePage(spec, nav, version, apiEndpoint)) + WriteFile(filepath.Join(docsDir, base+"_params_create.md"), buildCreateParamsPage(spec, nav)) + WriteFile(filepath.Join(docsDir, base+"_params_modify.md"), buildModifyParamsPage(spec, nav)) + WriteFile(filepath.Join(docsDir, base+"_outputs.md"), buildOutputsPage(spec, nav)) + WriteFile(filepath.Join(docsDir, base+"_ops.md"), buildOpsPage(spec, nav, docsDir)) + WriteFile(filepath.Join(docsDir, base+"_params.md"), buildParamsLandingPage(spec, nav)) + + for _, srName := range CollectSubresources(spec) { + srBase := fmt.Sprintf("%s_%s", base, Slug(srName)) + srNav := fmt.Sprintf("[%s (основной)](%s.md) | [Operations](%s_ops.md) | [%s](%s.md) | [Example](%s_example.md)", + spec.ServiceDisplayName, base, base, Capitalize(srName), srBase, srBase) + WriteFile(filepath.Join(docsDir, srBase+".md"), buildSubresourcePage(spec, srName, srNav)) + WriteFile(filepath.Join(docsDir, srBase+"_example.md"), buildSubresourceExamplePage(spec, srName, srNav, version, apiEndpoint)) + } +} + +// WriteFile пишет контент в файл. +func WriteFile(path, content string) { + if err := os.WriteFile(path, []byte(content), 0o644); err != nil { + fmt.Fprintf(os.Stderr, "ERROR: cannot write %s: %v\n", path, err) + os.Exit(1) + } +} + +// IndexMD генерирует index.md. +func IndexMD(docsDir string, specs []types.ServiceSpec) { + var b bytes.Buffer + b.WriteString("# Ресурсы провайдера\n\n") + b.WriteString("| ID | Ресурс | Описание |\n") + b.WriteString("|-----|--------|----------|\n") + for _, spec := range specs { + resName := "nubes_" + spec.Name + link := fmt.Sprintf("[%s](%s.md)", resName, spec.Name) + descr := strings.ReplaceAll(spec.ServiceDisplayName, "|", "\\|") + b.WriteString(fmt.Sprintf("| %d | %s | %s |\n", spec.ServiceID, link, descr)) + } + outPath := filepath.Join(docsDir, "index.md") + if err := os.WriteFile(outPath, b.Bytes(), 0o644); err != nil { + fmt.Fprintf(os.Stderr, "Warning: failed to write index.md: %v\n", err) + } +} + +// CollectSubresources возвращает уникальные subresource-имена. +func CollectSubresources(spec types.ServiceSpec) []string { + seen := map[string]bool{} + var out []string + for _, op := range spec.Operations { + if op.Kind == "subresource" && op.Subresource != "" { + if !seen[op.Subresource] { + seen[op.Subresource] = true + out = append(out, op.Subresource) + } + } + } + return out +} + +// FindParams находит параметры операции по action. +func FindParams(ops []types.OperationSpec, action string) []types.ParamSpec { + for _, op := range ops { + if op.Kind == "instance" && op.Action == action { + return op.Params + } + } + return nil +} + +// SplitParams делит параметры на обязательные и со значением по умолчанию. +func SplitParams(params []types.ParamSpec) ([]types.ParamSpec, []types.ParamSpec) { + required := []types.ParamSpec{} + defaults := []types.ParamSpec{} + for _, p := range params { + if hasDefault(p.Default) { + defaults = append(defaults, p) + continue + } + required = append(required, p) + } + return required, defaults +} + +// FindSubresourceParams находит параметры subresource-операции. +func FindSubresourceParams(ops []types.OperationSpec, srName, action string) []types.ParamSpec { + for _, op := range ops { + if op.Kind == "subresource" && op.Subresource == srName && op.Action == action { + return op.Params + } + } + return nil +} + +// ===== internal helpers ===== + +func buildHeader(spec types.ServiceSpec, nav string) string { + name := spec.ServiceDisplayName + if name == "" { + name = spec.Name + } + return fmt.Sprintf("# Resource nubes_%s\n\nService ID: `%d`\n\nService Name: %s\n\n%s\n\n", spec.Name, spec.ServiceID, name, nav) +} + +func buildManualPage(spec types.ServiceSpec, nav string) string { + var b strings.Builder + b.WriteString(buildHeader(spec, nav)) + b.WriteString("## MAN\n\n") + man := strings.TrimSpace(spec.ServiceMan) + if man == "" { + man = "Manual not available." + } + b.WriteString("
\n") + b.WriteString(man) + b.WriteString("\n
\n") + return b.String() +} + +func buildExamplePage(spec types.ServiceSpec, nav, version string, apiEndpoint string) string { + var b strings.Builder + b.WriteString(buildHeader(spec, nav)) + b.WriteString(fmt.Sprintf("## Copy-ready manifest (`%s_main.tf`)\n\n", spec.Name)) + b.WriteString("```hcl\n") + b.WriteString(exampleBlock(spec, version, apiEndpoint)) + b.WriteString("```\n\n") + b.WriteString("## Outputs usage\n\n") + b.WriteString("```hcl\n") + b.WriteString("# Выходные параметры можно использовать как\n") + b.WriteString(fmt.Sprintf("nubes_%s.baza.state_params[\"имя_ключа\"]\n", spec.Name)) + b.WriteString(fmt.Sprintf("nubes_%s.baza.state_out[\"имя_ключа\"]\n", spec.Name)) + b.WriteString(fmt.Sprintf("nubes_%s.baza.state_params_flat[\"имя_ключа\"]\n", spec.Name)) + b.WriteString(fmt.Sprintf("nubes_%s.baza.state_out_flat[\"имя_ключа\"]\n", spec.Name)) + b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_secrets[\"имя_ключа\"]\n", spec.Name)) + b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_url\n", spec.Name)) + b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_user_path\n", spec.Name)) + b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_fields\n", spec.Name)) + b.WriteString("```\n") + return b.String() +} + +func exampleBlock(spec types.ServiceSpec, version string, apiEndpoint string) string { + createParams := FindParams(spec.Operations, "create") + requiredParams, defaultParams := SplitParams(createParams) + + var b strings.Builder + b.WriteString("terraform {\n") + b.WriteString(" required_providers {\n") + b.WriteString(" nubes = {\n") + b.WriteString(" source = \"terra.k8c.ru/nubes/nubes\"\n") + b.WriteString(fmt.Sprintf(" version = \"%s\"\n", version)) + b.WriteString(" }\n") + b.WriteString(" }\n") + b.WriteString("}\n\n") + b.WriteString("# Токен доступа api_token к Nubes API — замените на реальный.\n") + b.WriteString("provider \"nubes\" {\n") + b.WriteString(" api_token = \"token_***\"\n") + b.WriteString(fmt.Sprintf(" api_endpoint = \"%s\"\n", apiEndpoint)) + b.WriteString("}\n") + b.WriteString(fmt.Sprintf("resource \"nubes_%s\" \"baza\" {\n", spec.Name)) + + for _, p := range requiredParams { + b.WriteString(formatParamLine(p, " ", true)) + } + + if len(defaultParams) > 0 { + b.WriteString("\n # Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте\n") + for _, p := range defaultParams { + b.WriteString(formatParamLine(p, " ", false)) + } + } + + b.WriteString("}\n") + return b.String() +} + +func buildCreateParamsPage(spec types.ServiceSpec, nav string) string { + var b strings.Builder + b.WriteString(buildHeader(spec, nav)) + b.WriteString("## Create params\n\n") + createParams := FindParams(spec.Operations, "create") + requiredParams, defaultParams := SplitParams(createParams) + + b.WriteString("**Обязательные параметры (вводимые пользователем)**\n\n") + b.WriteString(renderParamTable(requiredParams, true, true)) + + b.WriteString("\n**Параметры, имеющие значение по умолчанию, если не меняете - эти параметры не обязательно прописывать в манифесте**\n") + b.WriteString(renderParamTable(defaultParams, false, false)) + + lifecycle := spec.Lifecycle + if lifecycle.SuspendOnDestroyDefault || lifecycle.AdoptExistingOnCreateDefault { + b.WriteString("\n
\n") + b.WriteString("Параметры поведения (кратко)
\n") + b.WriteString(fmt.Sprintf("По умолчанию: `suspend_on_destroy = %s`, `adopt_existing_on_create = %s`.
\n", formatBoolTitle(lifecycle.SuspendOnDestroyDefault), formatBoolTitle(lifecycle.AdoptExistingOnCreateDefault))) + b.WriteString("При `terraform destroy` или удалении ресурса из манифеста:
\n") + b.WriteString("- `suspend_on_destroy=true` — инстанс переводится в `Suspend` (не удаляется).
\n") + b.WriteString("- `suspend_on_destroy=false` — Terraform удаляет ресурс только из state.
\n") + b.WriteString("При `apply` флаг `adopt_existing_on_create` работает как авто-`import`:
\n") + b.WriteString("- `false` — если ресурс уже есть, будет ошибка.
\n") + b.WriteString("- `true` — Terraform может взять существующий инстанс под управление (`running` → adopt, `suspended` → resume+adopt при совпадении параметров).
\n") + b.WriteString("Важно: один инстанс должен быть только в одном state. Иначе получите конфликт управления.\n") + b.WriteString("
\n") + } + + return b.String() +} + +func buildModifyParamsPage(spec types.ServiceSpec, nav string) string { + var b strings.Builder + b.WriteString(buildHeader(spec, nav)) + b.WriteString("## Modify params\n\n") + modifyParams := FindParams(spec.Operations, "modify") + b.WriteString(renderModifyTable(modifyParams)) + return b.String() +} + +func buildOutputsPage(spec types.ServiceSpec, nav string) string { + var b strings.Builder + b.WriteString(buildHeader(spec, nav)) + b.WriteString("## Output params\n\n") + if len(spec.Outputs.Params) == 0 { + b.WriteString("None.\n") + return b.String() + } + for _, p := range spec.Outputs.Params { + desc := outputDescription(p.Code) + if desc != "" { + b.WriteString(fmt.Sprintf("- `%s` (%s) — %s\n", p.Code, p.Type, desc)) + } else { + b.WriteString(fmt.Sprintf("- `%s` (%s)\n", p.Code, p.Type)) + } + } + + snapshot, ok := CloudOutputByServiceID[spec.ServiceID] + if !ok { + return b.String() + } + + hasOut := len(snapshot.OutPaths) > 0 + hasVault := len(snapshot.VaultFields) > 0 + if !hasOut && !hasVault { + return b.String() + } + + b.WriteString("\n## Реальные поля из облака (running/suspended snapshot)\n\n") + b.WriteString("Поля ниже получены из реальных инстансов этого сервиса в облаке.\n") + b.WriteString("Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`.\n\n") + + if hasOut { + b.WriteString("### `state_out_flat` ключи\n\n") + for _, key := range snapshot.OutPaths { + if strings.TrimSpace(key) == "" { + continue + } + b.WriteString(fmt.Sprintf("- `state_out_flat[\"%s\"]`\n", key)) + } + b.WriteString("\n") + } + + if hasVault { + b.WriteString("### `vault_secrets` ключи\n\n") + for _, key := range snapshot.VaultFields { + if strings.TrimSpace(key) == "" { + continue + } + b.WriteString(fmt.Sprintf("- `vault_secrets[\"%s\"]`\n", key)) + } + b.WriteString("\n") + } + + if spec.ServiceID == 90 && containsString(snapshot.OutPaths, "internalConnect.master") && containsString(snapshot.VaultFields, "adminUser") && containsString(snapshot.VaultFields, "adminPass") { + b.WriteString("### Пример для связки с Lucee/NodeJS\n\n") + b.WriteString("```hcl\n") + b.WriteString("testds_connectionString = \"jdbc:postgresql://${nubes_postgres.db2.state_out_flat[\"internalConnect.master\"]}:5432/postgres?sslmode=require\"\n") + b.WriteString("testds_username = nubes_postgres.db2.vault_secrets[\"adminUser\"]\n") + b.WriteString("testds_password = nubes_postgres.db2.vault_secrets[\"adminPass\"]\n") + b.WriteString("```\n") + } + + return b.String() +} + +func containsString(values []string, target string) bool { + for _, value := range values { + if value == target { + return true + } + } + return false +} + +func buildOpsPage(spec types.ServiceSpec, nav, docsDir string) string { + var b strings.Builder + b.WriteString(buildHeader(spec, nav)) + b.WriteString("## Operations\n\n") + for _, op := range spec.Operations { + name := op.Name + if name == "" { + name = op.Action + } + descr := strings.TrimSpace(stripHTML(op.Man)) + if descr != "" { + descr = ": " + descr + } + if op.Kind == "subresource" { + descrStr := strings.TrimPrefix(descr, ": ") + if op.Subresource != "" { + subFile := fmt.Sprintf("%s_%s.md", spec.Name, Slug(op.Subresource)) + b.WriteString(fmt.Sprintf("- `%s` — %s. См. [nubes_%s_%s](%s)\n", name, descrStr, spec.Name, Slug(op.Subresource), subFile)) + } else { + if descrStr != "" { + b.WriteString(fmt.Sprintf("\n#### `%s` — %s\n\n", name, descrStr)) + } else { + b.WriteString(fmt.Sprintf("\n#### `%s`\n\n", name)) + } + if len(op.Params) > 0 { + b.WriteString(renderParamTable(op.Params, true, true)) + b.WriteString("\n") + } + } + continue + } + + switch op.Action { + case "create": + b.WriteString(fmt.Sprintf("- `%s` — Создание кластера. Параметры: [%s params](%s_params_create.md)\n", name, "Create", spec.Name)) + case "modify": + b.WriteString(fmt.Sprintf("- `%s` — Изменение кластера. Параметры: [%s params](%s_params_modify.md)\n", name, "Modify", spec.Name)) + default: + if descr != "" { + b.WriteString(fmt.Sprintf("- `%s` — %s\n", name, strings.TrimPrefix(descr, ": "))) + } else { + b.WriteString(fmt.Sprintf("- `%s`\n", name)) + } + } + } + return b.String() +} + +func buildParamsLandingPage(spec types.ServiceSpec, nav string) string { + var b strings.Builder + b.WriteString(buildHeader(spec, nav)) + b.WriteString("## Parameters\n\n") + b.WriteString(fmt.Sprintf("- [Create params](%s_params_create.md)\n", spec.Name)) + b.WriteString(fmt.Sprintf("- [Modify params](%s_params_modify.md)\n", spec.Name)) + return b.String() +} + +func buildSubresourcePage(spec types.ServiceSpec, srName string, nav string) string { + var b strings.Builder + srResourceName := fmt.Sprintf("nubes_%s_%s", spec.Name, Slug(srName)) + b.WriteString(fmt.Sprintf("# Resource %s\n\n", srResourceName)) + b.WriteString(fmt.Sprintf("Service: `nubes_%s` (ID: %d)\n\n", spec.Name, spec.ServiceID)) + b.WriteString(nav + "\n\n") + + createParams := FindSubresourceParams(spec.Operations, srName, "create") + b.WriteString("## Create params\n\n") + if len(createParams) > 0 { + req, def := SplitParams(createParams) + b.WriteString("**Обязательные параметры**\n\n") + b.WriteString(renderParamTable(req, true, true)) + if len(def) > 0 { + b.WriteString("\n**Параметры со значением по умолчанию**\n\n") + b.WriteString(renderParamTable(def, false, false)) + } + } else { + b.WriteString("None.\n") + } + + modifyParams := FindSubresourceParams(spec.Operations, srName, "modify") + if len(modifyParams) > 0 { + b.WriteString("\n## Modify params\n\n") + b.WriteString(renderModifyTable(modifyParams)) + } + + deleteParams := FindSubresourceParams(spec.Operations, srName, "delete") + b.WriteString("\n## Delete params\n\n") + if len(deleteParams) > 0 { + b.WriteString(renderParamTable(deleteParams, true, true)) + } else { + b.WriteString("None.\n") + } + + return b.String() +} + +func buildSubresourceExamplePage(spec types.ServiceSpec, srName string, nav string, version string, apiEndpoint string) string { + var b strings.Builder + srResourceName := fmt.Sprintf("nubes_%s_%s", spec.Name, Slug(srName)) + b.WriteString(fmt.Sprintf("# Resource %s — Example\n\n", srResourceName)) + b.WriteString(fmt.Sprintf("Service: `nubes_%s` (ID: %d)\n\n", spec.Name, spec.ServiceID)) + b.WriteString(nav + "\n\n") + b.WriteString(fmt.Sprintf("## Copy-ready manifest (`%s_%s_main.tf`)\n\n", spec.Name, Slug(srName))) + b.WriteString("```hcl\n") + + b.WriteString("terraform {\n") + b.WriteString(" required_providers {\n") + b.WriteString(" nubes = {\n") + b.WriteString(" source = \"terra.k8c.ru/nubes/nubes\"\n") + b.WriteString(fmt.Sprintf(" version = \"%s\"\n", version)) + b.WriteString(" }\n") + b.WriteString(" }\n") + b.WriteString("}\n\n") + + b.WriteString("provider \"nubes\" {\n") + b.WriteString(" api_token = \"token_***\"\n") + b.WriteString(fmt.Sprintf(" api_endpoint = \"%s\"\n", apiEndpoint)) + b.WriteString("}\n\n") + + b.WriteString(fmt.Sprintf("# Родительский ресурс — nubes_%s должен быть создан заранее\n", spec.Name)) + b.WriteString(fmt.Sprintf("# resource \"nubes_%s\" \"baza\" { ... }\n\n", spec.Name)) + + b.WriteString(fmt.Sprintf("resource \"%s\" \"example\" {\n", srResourceName)) + b.WriteString(fmt.Sprintf(" %s_id = nubes_%s.baza.id\n", spec.Name, spec.Name)) + + createParams := FindSubresourceParams(spec.Operations, srName, "create") + req, def := SplitParams(createParams) + for _, p := range req { + b.WriteString(formatParamLine(p, " ", true)) + } + if len(def) > 0 { + b.WriteString("\n # Параметры со значением по умолчанию\n") + for _, p := range def { + b.WriteString(formatParamLine(p, " ", false)) + } + } + + b.WriteString("}\n") + b.WriteString("```\n") + return b.String() +} + +// ===== formatting helpers ===== + +func renderParamTable(params []types.ParamSpec, requiredTable, noDefault bool) string { + if len(params) == 0 { + return "None.\n" + } + var b strings.Builder + tableClass := "resource-table resource-table-compact" + if requiredTable { + tableClass += " resource-table-required" + } + b.WriteString(fmt.Sprintf("\n", tableClass)) + if noDefault { + b.WriteString("\n\n") + } else { + b.WriteString("\n\n") + } + for _, p := range params { + b.WriteString("") + b.WriteString(fmt.Sprintf("", escapeText(formatID(p.ID)))) + b.WriteString(fmt.Sprintf("", formatParamCode(p.Code))) + b.WriteString(fmt.Sprintf("", formatTypeCell(p))) + if !noDefault { + b.WriteString(fmt.Sprintf("", defaultCell(p.Default))) + } + b.WriteString(fmt.Sprintf("", escapeText(pickTextTable(p)))) + b.WriteString(fmt.Sprintf("", escapeText(collectConstraints(p)))) + b.WriteString("\n") + } + b.WriteString("
IDCodeTypeDescriptionConstraints
IDCodeTypeDefaultDescriptionConstraints
%s%s%s%s%s%s
\n") + return b.String() +} + +func renderModifyTable(params []types.ParamSpec) string { + if len(params) == 0 { + return "None.\n" + } + var b strings.Builder + b.WriteString("\n") + b.WriteString("\n\n") + for _, p := range params { + b.WriteString("") + b.WriteString(fmt.Sprintf("", escapeText(formatID(p.ID)))) + b.WriteString(fmt.Sprintf("", formatParamCode(p.Code))) + b.WriteString(fmt.Sprintf("", formatTypeCell(p))) + b.WriteString(fmt.Sprintf("", escapeText(pickTextTable(p)))) + b.WriteString(fmt.Sprintf("", escapeText(collectConstraints(p)))) + b.WriteString("\n") + } + b.WriteString("
IDCodeTypeDescriptionConstraints
%s%s%s%s%s
\n") + return b.String() +} + +func formatParamLine(p types.ParamSpec, indent string, requiredOnly bool) string { + if requiredOnly && !p.Required { + return "" + } + value := sampleValue(p) + comment := strings.TrimSpace(stripHTML(p.Descr)) + if comment == "" { + comment = strings.TrimSpace(stripHTML(p.Man)) + } + paramCode := ToSnake(p.Code) + if comment != "" { + return fmt.Sprintf("%s%s = %s # %s\n", indent, paramCode, value, comment) + } + return fmt.Sprintf("%s%s = %s\n", indent, paramCode, value) +} + +func sampleValue(p types.ParamSpec) string { + if hasDefault(p.Default) { + return formatLiteral(p.Default) + } + if len(p.ValueList) > 0 { + return formatLiteral(p.ValueList[0]) + } + dtype := strings.ToLower(firstType(p)) + switch { + case strings.Contains(dtype, "bool"): + return "false" + case strings.Contains(dtype, "int") || strings.Contains(dtype, "number"): + return "1" + case strings.Contains(dtype, "json"): + return "{}" + } + return "\"TODO\"" +} + +func formatLiteral(value interface{}) string { + switch v := value.(type) { + case bool: + if v { + return "true" + } + return "false" + case int: + return strconv.Itoa(v) + case int64: + return strconv.FormatInt(v, 10) + case float64: + return strconv.FormatFloat(v, 'f', -1, 64) + case string: + if strings.HasPrefix(v, "{") || strings.HasPrefix(v, "[") { + return v + } + if strings.HasPrefix(v, "\"") && strings.HasSuffix(v, "\"") { + return v + } + if looksLikeNumber(v) || v == "true" || v == "false" { + return v + } + return fmt.Sprintf("\"%s\"", v) + default: + return fmt.Sprintf("\"%v\"", v) + } +} + +func looksLikeNumber(value string) bool { + if value == "" { + return false + } + if value[0] == '-' { + value = value[1:] + } + for _, ch := range value { + if ch == '.' { + continue + } + if ch < '0' || ch > '9' { + return false + } + } + return true +} + +func formatID(id int) string { + if id == 0 { + return "" + } + return strconv.Itoa(id) +} + +func formatTypeCell(p types.ParamSpec) string { + dtype := firstType(p) + if dtype == "" { + return "" + } + return "" + escapeText(dtype) + "" +} + +func firstType(p types.ParamSpec) string { + if p.DataType != "" { + return normalizeType(p.DataType) + } + return normalizeType(p.Type) +} + +func normalizeType(dtype string) string { + cleaned := strings.ReplaceAll(dtype, ">", ">") + if idx := strings.Index(cleaned, ">"); idx >= 0 { + cleaned = cleaned[:idx] + } + return strings.TrimSpace(cleaned) +} + +func defaultCell(value interface{}) string { + if !hasDefault(value) { + return "" + } + if s, ok := value.(string); ok { + return "" + escapeText(s) + "" + } + return "" + escapeText(formatLiteral(value)) + "" +} + +func pickTextTable(p types.ParamSpec) string { + if p.Man != "" { + return p.Man + } + return p.Descr +} + +func hasDefault(value interface{}) bool { + if value == nil { + return false + } + if s, ok := value.(string); ok { + return strings.TrimSpace(s) != "" + } + return true +} + +func outputDescription(code string) string { + switch code { + case "state_params": + return "параметры, отправленные в API при создании/изменении" + case "state_out": + return "ответ API с результатами/выходными значениями" + case "state_params_flat": + return "параметры в плоском виде (ключи с путями)" + case "state_out_flat": + return "ответы в плоском виде (ключи с путями)" + case "vault_secrets": + return "секреты, записанные в Vault для ресурса" + case "vault_url": + return "адрес Vault для ресурса" + case "vault_user_path": + return "путь пользователя в Vault" + case "vault_fields": + return "список ключей доступных секретов" + default: + return "" + } +} + +func formatBoolTitle(value bool) string { + if value { + return "True" + } + return "False" +} + +func collectConstraints(p types.ParamSpec) string { + parts := []string{} + if p.MinValue != nil { + parts = append(parts, fmt.Sprintf("minvalue=%d", *p.MinValue)) + } + if p.MaxValue != nil { + parts = append(parts, fmt.Sprintf("maxvalue=%d", *p.MaxValue)) + } + if p.Regex != "" { + parts = append(parts, fmt.Sprintf("regex=%s", p.Regex)) + } + if len(p.ValueList) > 0 { + parts = append(parts, fmt.Sprintf("value_list=%s", strings.Join(p.ValueList, ", "))) + } + if p.Func != "" { + parts = append(parts, fmt.Sprintf("func=%s", p.Func)) + } + if p.RefSvcID != nil { + parts = append(parts, fmt.Sprintf("ref_svc_id=%d", *p.RefSvcID)) + } + if p.Unique != "" { + parts = append(parts, fmt.Sprintf("unique_scope=%s", p.Unique)) + } + if p.MaxLength != nil { + parts = append(parts, fmt.Sprintf("maxlength=%d", *p.MaxLength)) + } + if p.MinLength != nil { + parts = append(parts, fmt.Sprintf("minlength=%d", *p.MinLength)) + } + return strings.Join(parts, "; ") +} + +func escapeText(value string) string { + if value == "" { + return "" + } + text := html.EscapeString(value) + text = strings.ReplaceAll(text, "<br/>", "
") + text = strings.ReplaceAll(text, "<br />", "
") + text = strings.ReplaceAll(text, "<br>", "
") + return text +} + +func formatParamCode(code string) string { + return formatCode(ToSnake(code)) +} + +func formatCode(code string) string { + if code == "" { + return "" + } + if len(code) <= 40 { + return "" + code + "" + } + out := []string{} + prev := rune(0) + for _, ch := range code { + if ch == '_' { + out = append(out, "_") + prev = ch + continue + } + if prev != 0 && (unicodeIsLower(prev) || unicodeIsDigit(prev)) && unicodeIsUpper(ch) { + out = append(out, "") + } + out = append(out, string(ch)) + prev = ch + } + return "" + strings.Join(out, "") + "" +} + +// ToSnake конвертирует CamelCase → snake_case. +func ToSnake(value string) string { + if value == "" { + return "" + } + var out []rune + lastUnderscore := false + prevLowerOrDigit := false + for _, r := range value { + if r >= 'A' && r <= 'Z' { + if prevLowerOrDigit && !lastUnderscore { + out = append(out, '_') + } + out = append(out, r+'a'-'A') + lastUnderscore = false + prevLowerOrDigit = false + continue + } + if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') { + out = append(out, r) + lastUnderscore = false + prevLowerOrDigit = true + continue + } + if !lastUnderscore && len(out) > 0 { + out = append(out, '_') + lastUnderscore = true + } + prevLowerOrDigit = false + } + return strings.Trim(string(out), "_") +} + +func unicodeIsLower(r rune) bool { return r >= 'a' && r <= 'z' } +func unicodeIsUpper(r rune) bool { return r >= 'A' && r <= 'Z' } +func unicodeIsDigit(r rune) bool { return r >= '0' && r <= '9' } + +func stripHTML(value string) string { + if value == "" { + return "" + } + re := regexp.MustCompile("<[^>]+>") + cleaned := re.ReplaceAllString(value, " ") + cleaned = strings.Join(strings.Fields(cleaned), " ") + return html.UnescapeString(cleaned) +} + +// Slug нормализует строку для использования в URL/имени файла. +func Slug(value string) string { + out := []rune{} + for _, ch := range value { + if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' { + out = append(out, ch) + } else if ch >= 'A' && ch <= 'Z' { + out = append(out, ch+'a'-'A') + } else { + out = append(out, '_') + } + } + return strings.Trim(string(out), "_") +} + +// Capitalize делает первую букву заглавной. +func Capitalize(s string) string { + if s == "" { + return s + } + return strings.ToUpper(s[:1]) + s[1:] +} + +// LoadCloudOutputSnapshot загружает снепшот облачных выходов. +func LoadCloudOutputSnapshot(root string) map[int]types.CloudOutputSnapshot { + out := map[int]types.CloudOutputSnapshot{} + base := filepath.Join(root, "docs", "70_api") + entries, err := os.ReadDir(base) + if err != nil { + return out + } + + latestDir := "" + for _, entry := range entries { + if !entry.IsDir() { + continue + } + name := entry.Name() + if strings.HasPrefix(name, "output_inventory_snapshot_") { + if name > latestDir { + latestDir = name + } + } + } + if latestDir == "" { + return out + } + + path := filepath.Join(base, latestDir, "running_suspended_output_fields_for_docs.json") + b, err := os.ReadFile(path) + if err != nil { + return out + } + + var items []types.CloudOutputSnapshot + if err := json.Unmarshal(b, &items); err != nil { + return out + } + + for _, item := range items { + out[item.ServiceID] = item + } + + return out +} diff --git a/TOOLS/docs-generator/main.go b/TOOLS/docs-generator/main.go index 63fad11..c419712 100644 --- a/TOOLS/docs-generator/main.go +++ b/TOOLS/docs-generator/main.go @@ -1,11 +1,9 @@ +// docs-generator — генератор Markdown-документации для ресурсов провайдера. package main import ( - "bytes" - "encoding/json" "flag" "fmt" - "html" "io/fs" "os" "path/filepath" @@ -15,91 +13,29 @@ import ( "strings" "gopkg.in/yaml.v3" + + "docs-generator/internal/types" + "docs-generator/internal/writers" ) -type ParamSpec struct { - ID int `yaml:"id"` - Code string `yaml:"code"` - DataType string `yaml:"data_type"` - Type string `yaml:"type"` - Required bool `yaml:"required"` - Default interface{} `yaml:"default"` - Descr string `yaml:"descr"` - Man string `yaml:"man"` - RefSvcID *int `yaml:"ref_svc_id"` - Func string `yaml:"func"` - MinValue *int `yaml:"minvalue"` - MaxValue *int `yaml:"maxvalue"` - Regex string `yaml:"regex"` - ValueList []string `yaml:"value_list"` - Unique string `yaml:"unique_scope"` - MaxLength *int `yaml:"maxlength"` - MinLength *int `yaml:"minlength"` -} - -type OperationSpec struct { - Name string `yaml:"name"` - ID int `yaml:"id"` - Kind string `yaml:"kind"` - Action string `yaml:"action"` - Subresource string `yaml:"subresource"` - Man string `yaml:"man"` - Params []ParamSpec `yaml:"params"` -} - -type OutputParam struct { - Code string `yaml:"code"` - Type string `yaml:"type"` - Sensitive bool `yaml:"sensitive"` -} - -type ServiceSpec struct { - Name string `yaml:"name"` - ServiceID int `yaml:"service_id"` - ServiceDisplayName string `yaml:"service_display_name"` - ServiceMan string `yaml:"service_man"` - Lifecycle struct { - SuspendOnDestroyDefault bool `yaml:"suspend_on_destroy_default"` - AdoptExistingOnCreateDefault bool `yaml:"adopt_existing_on_create_default"` - } `yaml:"lifecycle"` - Outputs struct { - Params []OutputParam `yaml:"params"` - } `yaml:"outputs"` - Operations []OperationSpec `yaml:"operations"` -} - -type ServiceMeta struct { - ID int - Name string -} - -type CloudOutputSnapshot struct { - ServiceID int `json:"serviceId"` - ServiceName string `json:"serviceName"` - OutPaths []string `json:"out_paths"` - VaultFields []string `json:"vault_fields"` -} - -var cloudOutputByServiceID = map[int]CloudOutputSnapshot{} - func main() { resourcesDirFlag := flag.String("resources", "", "Path to resources_yaml") docsDirFlag := flag.String("docs", "", "Path to docs/30_registry/resources") servicesListFlag := flag.String("services", "", "Path to devops/config/services_list.txt") - excludeFlag := flag.String("exclude", "", "Comma-separated resource names to skip (e.g. clickhouse)") + excludeFlag := flag.String("exclude", "", "Comma-separated resource names to skip") versionFlag := flag.String("version", "", "Provider version for example block") apiEndpointFlag := flag.String("api-endpoint", "https://deck-api.ngcloud.ru/api/v1/index.cfm", "API endpoint for example block") flag.Parse() root := detectRoot() - resourcesDir := pickPath(*resourcesDirFlag, filepath.Join(root, "universal_rebuild", "resources_yaml")) + resourcesDir := pickPath(*resourcesDirFlag, filepath.Join(root, "provider", "resources_yaml")) docsDir := pickPath(*docsDirFlag, filepath.Join(root, "docs", "30_registry", "resources")) servicesList := pickPath(*servicesListFlag, filepath.Join(root, "devops", "config", "services_list.txt")) - cloudOutputByServiceID = loadCloudOutputSnapshot(root) + writers.CloudOutputByServiceID = writers.LoadCloudOutputSnapshot(root) excludeSet := toSet(*excludeFlag) version := *versionFlag if version == "" { - version = detectVersion(filepath.Join(root, "universal_rebuild", "main.go")) + version = detectVersion(filepath.Join(root, "provider", "main.go")) } apiEndpoint := *apiEndpointFlag @@ -115,24 +51,23 @@ func main() { os.Exit(1) } - var processedSpecs []ServiceSpec + var processedSpecs []types.ServiceSpec for _, spec := range specs { if excludeSet[spec.Name] { continue } - writeResourceDocs(docsDir, spec, version, apiEndpoint) + writers.ResourceDocs(docsDir, spec, version, apiEndpoint) processedSpecs = append(processedSpecs, spec) } - generateIndexMD(docsDir, processedSpecs) + writers.IndexMD(docsDir, processedSpecs) } func detectRoot() string { cwd, err := os.Getwd() if err != nil { - fmt.Fprintf(os.Stderr, "ERROR: cannot get working directory: %v\n", err) - os.Exit(1) + panic(err) } - if filepath.Base(cwd) == "universal_rebuild" { + if filepath.Base(cwd) == "provider" { return filepath.Dir(cwd) } return cwd @@ -157,48 +92,6 @@ func toSet(csv string) map[string]bool { return out } -func loadCloudOutputSnapshot(root string) map[int]CloudOutputSnapshot { - out := map[int]CloudOutputSnapshot{} - base := filepath.Join(root, "docs", "70_api") - entries, err := os.ReadDir(base) - if err != nil { - return out - } - - latestDir := "" - for _, entry := range entries { - if !entry.IsDir() { - continue - } - name := entry.Name() - if strings.HasPrefix(name, "output_inventory_snapshot_") { - if name > latestDir { - latestDir = name - } - } - } - if latestDir == "" { - return out - } - - path := filepath.Join(base, latestDir, "running_suspended_output_fields_for_docs.json") - b, err := os.ReadFile(path) - if err != nil { - return out - } - - var items []CloudOutputSnapshot - if err := json.Unmarshal(b, &items); err != nil { - return out - } - - for _, item := range items { - out[item.ServiceID] = item - } - - return out -} - func detectVersion(mainPath string) string { b, err := os.ReadFile(mainPath) if err != nil { @@ -212,13 +105,13 @@ func detectVersion(mainPath string) string { return "2.x" } -func loadServicesList(path string) []ServiceMeta { +func loadServicesList(path string) []types.ServiceMeta { b, err := os.ReadFile(path) if err != nil { return nil } lines := strings.Split(string(b), "\n") - out := []ServiceMeta{} + out := []types.ServiceMeta{} for _, line := range lines { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "#") { @@ -233,14 +126,14 @@ func loadServicesList(path string) []ServiceMeta { if err != nil { continue } - out = append(out, ServiceMeta{ID: id, Name: parts[1]}) + out = append(out, types.ServiceMeta{ID: id, Name: parts[1]}) } return out } -func loadSpecs(dir string, ordered []ServiceMeta) []ServiceSpec { - specsByID := map[int]ServiceSpec{} - walkErr := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { +func loadSpecs(dir string, ordered []types.ServiceMeta) []types.ServiceSpec { + specsByID := map[int]types.ServiceSpec{} + filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } @@ -251,20 +144,16 @@ func loadSpecs(dir string, ordered []ServiceMeta) []ServiceSpec { if err != nil { return err } - var spec ServiceSpec + var spec types.ServiceSpec if err := yaml.Unmarshal(b, &spec); err != nil { return err } specsByID[spec.ServiceID] = spec return nil }) - if walkErr != nil { - fmt.Fprintf(os.Stderr, "ERROR: failed to load specs from %s: %v\n", dir, walkErr) - os.Exit(1) - } if len(ordered) == 0 { - specs := make([]ServiceSpec, 0, len(specsByID)) + specs := make([]types.ServiceSpec, 0, len(specsByID)) for _, spec := range specsByID { specs = append(specs, spec) } @@ -272,7 +161,7 @@ func loadSpecs(dir string, ordered []ServiceMeta) []ServiceSpec { return specs } - specs := []ServiceSpec{} + specs := []types.ServiceSpec{} for _, meta := range ordered { spec, ok := specsByID[meta.ID] if !ok { @@ -282,818 +171,3 @@ func loadSpecs(dir string, ordered []ServiceMeta) []ServiceSpec { } return specs } - -// collectSubresources возвращает уникальные subresource-имена сервиса. -func collectSubresources(spec ServiceSpec) []string { - seen := map[string]bool{} - var out []string - for _, op := range spec.Operations { - if op.Kind == "subresource" && op.Subresource != "" { - if !seen[op.Subresource] { - seen[op.Subresource] = true - out = append(out, op.Subresource) - } - } - } - return out -} - -func writeResourceDocs(docsDir string, spec ServiceSpec, version string, apiEndpoint string) { - base := spec.Name - nav := fmt.Sprintf("[Manual](%s.md) | [Create params](%s_params_create.md) | [Modify params](%s_params_modify.md) | [Output params](%s_outputs.md) | [Operations](%s_ops.md) | [Example](%s_example.md)", base, base, base, base, base, base) - - writeFile(filepath.Join(docsDir, base+".md"), buildManualPage(spec, nav)) - writeFile(filepath.Join(docsDir, base+"_example.md"), buildExamplePage(spec, nav, version, apiEndpoint)) - writeFile(filepath.Join(docsDir, base+"_params_create.md"), buildCreateParamsPage(spec, nav)) - writeFile(filepath.Join(docsDir, base+"_params_modify.md"), buildModifyParamsPage(spec, nav)) - writeFile(filepath.Join(docsDir, base+"_outputs.md"), buildOutputsPage(spec, nav)) - writeFile(filepath.Join(docsDir, base+"_ops.md"), buildOpsPage(spec, nav, docsDir)) - writeFile(filepath.Join(docsDir, base+"_params.md"), buildParamsLandingPage(spec, nav)) - - // Отдельные страницы для каждого subresource (nubes_{service}_{subresource}) - for _, srName := range collectSubresources(spec) { - srBase := fmt.Sprintf("%s_%s", base, slug(srName)) - srNav := fmt.Sprintf("[%s (основной)](%s.md) | [Operations](%s_ops.md) | [%s](%s.md) | [Example](%s_example.md)", - spec.ServiceDisplayName, base, base, capitalize(srName), srBase, srBase) - writeFile(filepath.Join(docsDir, srBase+".md"), buildSubresourcePage(spec, srName, srNav)) - writeFile(filepath.Join(docsDir, srBase+"_example.md"), buildSubresourceExamplePage(spec, srName, srNav, version, apiEndpoint)) - } -} - -func writeFile(path, content string) { - if err := os.WriteFile(path, []byte(content), 0o644); err != nil { - fmt.Fprintf(os.Stderr, "ERROR: cannot write %s: %v\n", path, err) - os.Exit(1) - } -} - -func buildHeader(spec ServiceSpec, nav string) string { - name := spec.ServiceDisplayName - if name == "" { - name = spec.Name - } - return fmt.Sprintf("# Resource nubes_%s\n\nService ID: `%d`\n\nService Name: %s\n\n%s\n\n", spec.Name, spec.ServiceID, name, nav) -} - -func buildManualPage(spec ServiceSpec, nav string) string { - var b strings.Builder - b.WriteString(buildHeader(spec, nav)) - b.WriteString("## MAN\n\n") - man := strings.TrimSpace(spec.ServiceMan) - if man == "" { - man = "Manual not available." - } - b.WriteString("
\n") - b.WriteString(man) - b.WriteString("\n
\n") - return b.String() -} - -func buildExamplePage(spec ServiceSpec, nav, version string, apiEndpoint string) string { - var b strings.Builder - b.WriteString(buildHeader(spec, nav)) - b.WriteString(fmt.Sprintf("## Copy-ready manifest (`%s_main.tf`)\n\n", spec.Name)) - b.WriteString("```hcl\n") - b.WriteString(exampleBlock(spec, version, apiEndpoint)) - b.WriteString("```\n\n") - b.WriteString("## Outputs usage\n\n") - b.WriteString("```hcl\n") - b.WriteString("# \u0412\u044b\u0445\u043e\u0434\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a\n") - b.WriteString(fmt.Sprintf("nubes_%s.baza.state_params[\"\u0438\u043c\u044f_\u043a\u043b\u044e\u0447\u0430\"]\n", spec.Name)) - b.WriteString(fmt.Sprintf("nubes_%s.baza.state_out[\"\u0438\u043c\u044f_\u043a\u043b\u044e\u0447\u0430\"]\n", spec.Name)) - b.WriteString(fmt.Sprintf("nubes_%s.baza.state_params_flat[\"\u0438\u043c\u044f_\u043a\u043b\u044e\u0447\u0430\"]\n", spec.Name)) - b.WriteString(fmt.Sprintf("nubes_%s.baza.state_out_flat[\"\u0438\u043c\u044f_\u043a\u043b\u044e\u0447\u0430\"]\n", spec.Name)) - b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_secrets[\"\u0438\u043c\u044f_\u043a\u043b\u044e\u0447\u0430\"]\n", spec.Name)) - b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_url\n", spec.Name)) - b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_user_path\n", spec.Name)) - b.WriteString(fmt.Sprintf("nubes_%s.baza.vault_fields\n", spec.Name)) - b.WriteString("```\n") - return b.String() -} - -func exampleBlock(spec ServiceSpec, version string, apiEndpoint string) string { - createParams := findParams(spec.Operations, "create") - requiredParams, defaultParams := splitParams(createParams) - - var b strings.Builder - b.WriteString("terraform {\n") - b.WriteString(" required_providers {\n") - b.WriteString(" nubes = {\n") - b.WriteString(" source = \"terra.k8c.ru/nubes/nubes\"\n") - b.WriteString(fmt.Sprintf(" version = \"%s\"\n", version)) - b.WriteString(" }\n") - b.WriteString(" }\n") - b.WriteString("}\n\n") - b.WriteString("# \u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 api_token \u043a Nubes API \u2014 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439.\n") - b.WriteString("provider \"nubes\" {\n") - b.WriteString(" api_token = \"token_***\"\n") - b.WriteString(fmt.Sprintf(" api_endpoint = \"%s\"\n", apiEndpoint)) - b.WriteString("}\n") - b.WriteString(fmt.Sprintf("resource \"nubes_%s\" \"baza\" {\n", spec.Name)) - - for _, p := range requiredParams { - b.WriteString(formatParamLine(p, " ", true)) - } - - if len(defaultParams) > 0 { - b.WriteString("\n # \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0438\u043c\u0435\u044e\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0435\u0441\u043b\u0438 \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0435 - \u044d\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435\n") - for _, p := range defaultParams { - b.WriteString(formatParamLine(p, " ", false)) - } - } - - b.WriteString("}\n") - return b.String() -} - -func buildCreateParamsPage(spec ServiceSpec, nav string) string { - var b strings.Builder - b.WriteString(buildHeader(spec, nav)) - b.WriteString("## Create params\n\n") - createParams := findParams(spec.Operations, "create") - requiredParams, defaultParams := splitParams(createParams) - - b.WriteString("**\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b (\u0432\u0432\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c)**\n\n") - b.WriteString(renderParamTable(requiredParams, true, true)) - - b.WriteString("\n**\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0438\u043c\u0435\u044e\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0435\u0441\u043b\u0438 \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0435 - \u044d\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435**\n") - b.WriteString(renderParamTable(defaultParams, false, false)) - - lifecycle := spec.Lifecycle - if lifecycle.SuspendOnDestroyDefault || lifecycle.AdoptExistingOnCreateDefault { - b.WriteString("\n
\n") - b.WriteString("\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f (\u043a\u0440\u0430\u0442\u043a\u043e)
\n") - b.WriteString(fmt.Sprintf("\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: `suspend_on_destroy = %s`, `adopt_existing_on_create = %s`.
\n", formatBoolTitle(lifecycle.SuspendOnDestroyDefault), formatBoolTitle(lifecycle.AdoptExistingOnCreateDefault))) - b.WriteString("\u041f\u0440\u0438 `terraform destroy` \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u0430 \u0438\u0437 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430:
\n") - b.WriteString("- `suspend_on_destroy=true` \u2014 \u0438\u043d\u0441\u0442\u0430\u043d\u0441 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0432 `Suspend` (\u043d\u0435 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f).
\n") - b.WriteString("- `suspend_on_destroy=false` \u2014 Terraform \u0443\u0434\u0430\u043b\u044f\u0435\u0442 \u0440\u0435\u0441\u0443\u0440\u0441 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437 state.
\n") - b.WriteString("\u041f\u0440\u0438 `apply` \u0444\u043b\u0430\u0433 `adopt_existing_on_create` \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0430\u0432\u0442\u043e-`import`:
\n") - b.WriteString("- `false` \u2014 \u0435\u0441\u043b\u0438 \u0440\u0435\u0441\u0443\u0440\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c, \u0431\u0443\u0434\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0430.
\n") - b.WriteString("- `true` \u2014 Terraform \u043c\u043e\u0436\u0435\u0442 \u0432\u0437\u044f\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0441 \u043f\u043e\u0434 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 (`running` \u2192 adopt, `suspended` \u2192 resume+adopt \u043f\u0440\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432).
\n") - b.WriteString("\u0412\u0430\u0436\u043d\u043e: \u043e\u0434\u0438\u043d \u0438\u043d\u0441\u0442\u0430\u043d\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043e\u0434\u043d\u043e\u043c state. \u0418\u043d\u0430\u0447\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f.\n") - b.WriteString("
\n") - } - - return b.String() -} - -func buildModifyParamsPage(spec ServiceSpec, nav string) string { - var b strings.Builder - b.WriteString(buildHeader(spec, nav)) - b.WriteString("## Modify params\n\n") - modifyParams := findParams(spec.Operations, "modify") - b.WriteString(renderModifyTable(modifyParams)) - return b.String() -} - -func buildOutputsPage(spec ServiceSpec, nav string) string { - var b strings.Builder - b.WriteString(buildHeader(spec, nav)) - b.WriteString("## Output params\n\n") - if len(spec.Outputs.Params) == 0 { - b.WriteString("None.\n") - return b.String() - } - for _, p := range spec.Outputs.Params { - desc := outputDescription(p.Code) - if desc != "" { - b.WriteString(fmt.Sprintf("- `%s` (%s) \u2014 %s\n", p.Code, p.Type, desc)) - } else { - b.WriteString(fmt.Sprintf("- `%s` (%s)\n", p.Code, p.Type)) - } - } - - snapshot, ok := cloudOutputByServiceID[spec.ServiceID] - if !ok { - return b.String() - } - - hasOut := len(snapshot.OutPaths) > 0 - hasVault := len(snapshot.VaultFields) > 0 - if !hasOut && !hasVault { - return b.String() - } - - b.WriteString("\n## Реальные поля из облака (running/suspended snapshot)\n\n") - b.WriteString("Поля ниже получены из реальных инстансов этого сервиса в облаке.\n") - b.WriteString("Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`.\n\n") - - if hasOut { - b.WriteString("### `state_out_flat` ключи\n\n") - for _, key := range snapshot.OutPaths { - if strings.TrimSpace(key) == "" { - continue - } - b.WriteString(fmt.Sprintf("- `state_out_flat[\"%s\"]`\n", key)) - } - b.WriteString("\n") - } - - if hasVault { - b.WriteString("### `vault_secrets` ключи\n\n") - for _, key := range snapshot.VaultFields { - if strings.TrimSpace(key) == "" { - continue - } - b.WriteString(fmt.Sprintf("- `vault_secrets[\"%s\"]`\n", key)) - } - b.WriteString("\n") - } - - if spec.ServiceID == 90 && containsString(snapshot.OutPaths, "internalConnect.master") && containsString(snapshot.VaultFields, "adminUser") && containsString(snapshot.VaultFields, "adminPass") { - b.WriteString("### Пример для связки с Lucee/NodeJS\n\n") - b.WriteString("```hcl\n") - b.WriteString("testds_connectionString = \"jdbc:postgresql://${nubes_postgres.db2.state_out_flat[\"internalConnect.master\"]}:5432/postgres?sslmode=require\"\n") - b.WriteString("testds_username = nubes_postgres.db2.vault_secrets[\"adminUser\"]\n") - b.WriteString("testds_password = nubes_postgres.db2.vault_secrets[\"adminPass\"]\n") - b.WriteString("```\n") - } - - return b.String() -} - -func containsString(values []string, target string) bool { - for _, value := range values { - if value == target { - return true - } - } - return false -} - -func buildOpsPage(spec ServiceSpec, nav, docsDir string) string { - var b strings.Builder - b.WriteString(buildHeader(spec, nav)) - b.WriteString("## Operations\n\n") - for _, op := range spec.Operations { - name := op.Name - if name == "" { - name = op.Action - } - descr := strings.TrimSpace(stripHTML(op.Man)) - if descr != "" { - descr = ": " + descr - } - // Subresource-операции проверяем ДО switch, чтобы create_user/create_database - // не попадали в case "create" основного ресурса - if op.Kind == "subresource" { - descrStr := strings.TrimPrefix(descr, ": ") - if op.Subresource != "" { - // Ссылаемся на отдельную страницу subresource — она будет создана в writeResourceDocs - subFile := fmt.Sprintf("%s_%s.md", spec.Name, slug(op.Subresource)) - b.WriteString(fmt.Sprintf("- `%s` \u2014 %s. \u0421\u043c. [nubes_%s_%s](%s)\n", name, descrStr, spec.Name, slug(op.Subresource), subFile)) - } else { - // Нет subresource-имени — выводим инлайн - if descrStr != "" { - b.WriteString(fmt.Sprintf("\n#### `%s` \u2014 %s\n\n", name, descrStr)) - } else { - b.WriteString(fmt.Sprintf("\n#### `%s`\n\n", name)) - } - if len(op.Params) > 0 { - b.WriteString(renderParamTable(op.Params, true, true)) - b.WriteString("\n") - } - } - continue - } - - switch op.Action { - case "create": - b.WriteString(fmt.Sprintf("- `%s` \u2014 \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b: [%s params](%s_params_create.md)\n", name, "Create", spec.Name)) - case "modify": - b.WriteString(fmt.Sprintf("- `%s` \u2014 \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b: [%s params](%s_params_modify.md)\n", name, "Modify", spec.Name)) - default: - if descr != "" { - b.WriteString(fmt.Sprintf("- `%s` \u2014 %s\n", name, strings.TrimPrefix(descr, ": "))) - } else { - b.WriteString(fmt.Sprintf("- `%s`\n", name)) - } - } - } - return b.String() -} - -func buildParamsLandingPage(spec ServiceSpec, nav string) string { - var b strings.Builder - b.WriteString(buildHeader(spec, nav)) - b.WriteString("## Parameters\n\n") - b.WriteString(fmt.Sprintf("- [Create params](%s_params_create.md)\n", spec.Name)) - b.WriteString(fmt.Sprintf("- [Modify params](%s_params_modify.md)\n", spec.Name)) - return b.String() -} - -func findParams(ops []OperationSpec, action string) []ParamSpec { - for _, op := range ops { - if op.Kind == "instance" && op.Action == action { - return op.Params - } - } - return nil -} - -func splitParams(params []ParamSpec) ([]ParamSpec, []ParamSpec) { - required := []ParamSpec{} - defaults := []ParamSpec{} - for _, p := range params { - if hasDefault(p.Default) { - defaults = append(defaults, p) - continue - } - required = append(required, p) - } - return required, defaults -} - -func hasDefault(value interface{}) bool { - if value == nil { - return false - } - if s, ok := value.(string); ok { - return strings.TrimSpace(s) != "" - } - return true -} - -func renderParamTable(params []ParamSpec, requiredTable, noDefault bool) string { - if len(params) == 0 { - return "None.\n" - } - var b strings.Builder - tableClass := "resource-table resource-table-compact" - if requiredTable { - tableClass += " resource-table-required" - } - b.WriteString(fmt.Sprintf("\n", tableClass)) - if noDefault { - b.WriteString("\n\n") - } else { - b.WriteString("\n\n") - } - for _, p := range params { - b.WriteString("") - b.WriteString(fmt.Sprintf("", escapeText(formatID(p.ID)))) - b.WriteString(fmt.Sprintf("", formatParamCode(p.Code))) - b.WriteString(fmt.Sprintf("", formatTypeCell(p))) - if !noDefault { - b.WriteString(fmt.Sprintf("", defaultCell(p.Default))) - } - b.WriteString(fmt.Sprintf("", escapeText(pickTextTable(p)))) - b.WriteString(fmt.Sprintf("", escapeText(collectConstraints(p)))) - b.WriteString("\n") - } - b.WriteString("
IDCodeTypeDescriptionConstraints
IDCodeTypeDefaultDescriptionConstraints
%s%s%s%s%s%s
\n") - return b.String() -} - -func renderModifyTable(params []ParamSpec) string { - if len(params) == 0 { - return "None.\n" - } - var b strings.Builder - b.WriteString("\n") - b.WriteString("\n\n") - for _, p := range params { - b.WriteString("") - b.WriteString(fmt.Sprintf("", escapeText(formatID(p.ID)))) - b.WriteString(fmt.Sprintf("", formatParamCode(p.Code))) - b.WriteString(fmt.Sprintf("", formatTypeCell(p))) - b.WriteString(fmt.Sprintf("", escapeText(pickTextTable(p)))) - b.WriteString(fmt.Sprintf("", escapeText(collectConstraints(p)))) - b.WriteString("\n") - } - b.WriteString("
IDCodeTypeDescriptionConstraints
%s%s%s%s%s
\n") - return b.String() -} - -func formatParamLine(p ParamSpec, indent string, requiredOnly bool) string { - if requiredOnly && !p.Required { - return "" - } - value := sampleValue(p) - comment := strings.TrimSpace(stripHTML(p.Descr)) - if comment == "" { - comment = strings.TrimSpace(stripHTML(p.Man)) - } - paramCode := toSnake(p.Code) - if comment != "" { - return fmt.Sprintf("%s%s = %s # %s\n", indent, paramCode, value, comment) - } - return fmt.Sprintf("%s%s = %s\n", indent, paramCode, value) -} - -func sampleValue(p ParamSpec) string { - if hasDefault(p.Default) { - return formatLiteral(p.Default) - } - if len(p.ValueList) > 0 { - return formatLiteral(p.ValueList[0]) - } - dtype := strings.ToLower(firstType(p)) - switch { - case strings.Contains(dtype, "bool"): - return "false" - case strings.Contains(dtype, "int") || strings.Contains(dtype, "number"): - return "1" - case strings.Contains(dtype, "json"): - return "{}" - } - return "\"TODO\"" -} - -func formatLiteral(value interface{}) string { - switch v := value.(type) { - case bool: - if v { - return "true" - } - return "false" - case int: - return strconv.Itoa(v) - case int64: - return strconv.FormatInt(v, 10) - case float64: - return strconv.FormatFloat(v, 'f', -1, 64) - case string: - if strings.HasPrefix(v, "{") || strings.HasPrefix(v, "[") { - return v - } - if strings.HasPrefix(v, "\"") && strings.HasSuffix(v, "\"") { - return v - } - if looksLikeNumber(v) || v == "true" || v == "false" { - return v - } - return fmt.Sprintf("\"%s\"", v) - default: - return fmt.Sprintf("\"%v\"", v) - } -} - -func looksLikeNumber(value string) bool { - if value == "" { - return false - } - if value[0] == '-' { - value = value[1:] - } - for _, ch := range value { - if ch == '.' { - continue - } - if ch < '0' || ch > '9' { - return false - } - } - return true -} - -func formatID(id int) string { - if id == 0 { - return "" - } - return strconv.Itoa(id) -} - -func formatTypeCell(p ParamSpec) string { - dtype := firstType(p) - if dtype == "" { - return "" - } - return "" + escapeText(dtype) + "" -} - -func firstType(p ParamSpec) string { - if p.DataType != "" { - return normalizeType(p.DataType) - } - return normalizeType(p.Type) -} - -func normalizeType(dtype string) string { - cleaned := strings.ReplaceAll(dtype, ">", ">") - if idx := strings.Index(cleaned, ">"); idx >= 0 { - cleaned = cleaned[:idx] - } - return strings.TrimSpace(cleaned) -} - -func defaultCell(value interface{}) string { - if !hasDefault(value) { - return "" - } - if s, ok := value.(string); ok { - return "" + escapeText(s) + "" - } - return "" + escapeText(formatLiteral(value)) + "" -} - -func pickTextTable(p ParamSpec) string { - if p.Man != "" { - return p.Man - } - return p.Descr -} - -func outputDescription(code string) string { - switch code { - case "state_params": - return "\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0432 API \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438/\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438" - case "state_out": - return "\u043e\u0442\u0432\u0435\u0442 API \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438/\u0432\u044b\u0445\u043e\u0434\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438" - case "state_params_flat": - return "\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432 \u043f\u043b\u043e\u0441\u043a\u043e\u043c \u0432\u0438\u0434\u0435 (\u043a\u043b\u044e\u0447\u0438 \u0441 \u043f\u0443\u0442\u044f\u043c\u0438)" - case "state_out_flat": - return "\u043e\u0442\u0432\u0435\u0442\u044b \u0432 \u043f\u043b\u043e\u0441\u043a\u043e\u043c \u0432\u0438\u0434\u0435 (\u043a\u043b\u044e\u0447\u0438 \u0441 \u043f\u0443\u0442\u044f\u043c\u0438)" - case "vault_secrets": - return "\u0441\u0435\u043a\u0440\u0435\u0442\u044b, \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0432 Vault \u0434\u043b\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u0430" - case "vault_url": - return "\u0430\u0434\u0440\u0435\u0441 Vault \u0434\u043b\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u0430" - case "vault_user_path": - return "\u043f\u0443\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 Vault" - case "vault_fields": - return "\u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043b\u044e\u0447\u0435\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0441\u0435\u043a\u0440\u0435\u0442\u043e\u0432" - default: - return "" - } -} - -func formatBoolTitle(value bool) string { - if value { - return "True" - } - return "False" -} - -func collectConstraints(p ParamSpec) string { - parts := []string{} - if p.MinValue != nil { - parts = append(parts, fmt.Sprintf("minvalue=%d", *p.MinValue)) - } - if p.MaxValue != nil { - parts = append(parts, fmt.Sprintf("maxvalue=%d", *p.MaxValue)) - } - if p.Regex != "" { - parts = append(parts, fmt.Sprintf("regex=%s", p.Regex)) - } - if len(p.ValueList) > 0 { - parts = append(parts, fmt.Sprintf("value_list=%s", strings.Join(p.ValueList, ", "))) - } - if p.Func != "" { - parts = append(parts, fmt.Sprintf("func=%s", p.Func)) - } - if p.RefSvcID != nil { - parts = append(parts, fmt.Sprintf("ref_svc_id=%d", *p.RefSvcID)) - } - if p.Unique != "" { - parts = append(parts, fmt.Sprintf("unique_scope=%s", p.Unique)) - } - if p.MaxLength != nil { - parts = append(parts, fmt.Sprintf("maxlength=%d", *p.MaxLength)) - } - if p.MinLength != nil { - parts = append(parts, fmt.Sprintf("minlength=%d", *p.MinLength)) - } - return strings.Join(parts, "; ") -} - -func escapeText(value string) string { - if value == "" { - return "" - } - text := html.EscapeString(value) - text = strings.ReplaceAll(text, "<br/>", "
") - text = strings.ReplaceAll(text, "<br />", "
") - text = strings.ReplaceAll(text, "<br>", "
") - return text -} - -func formatCode(code string) string { - if code == "" { - return "" - } - if len(code) <= 40 { - return "" + code + "" - } - out := []string{} - prev := rune(0) - for _, ch := range code { - if ch == '_' { - out = append(out, "_") - prev = ch - continue - } - if prev != 0 && (unicodeIsLower(prev) || unicodeIsDigit(prev)) && unicodeIsUpper(ch) { - out = append(out, "") - } - out = append(out, string(ch)) - prev = ch - } - return "" + strings.Join(out, "") + "" -} - -func formatParamCode(code string) string { - return formatCode(toSnake(code)) -} - -func toSnake(value string) string { - if value == "" { - return "" - } - var out []rune - lastUnderscore := false - prevLowerOrDigit := false - for _, r := range value { - if r >= 'A' && r <= 'Z' { - if prevLowerOrDigit && !lastUnderscore { - out = append(out, '_') - } - out = append(out, r+'a'-'A') - lastUnderscore = false - prevLowerOrDigit = false - continue - } - if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') { - out = append(out, r) - lastUnderscore = false - prevLowerOrDigit = true - continue - } - if !lastUnderscore && len(out) > 0 { - out = append(out, '_') - lastUnderscore = true - } - prevLowerOrDigit = false - } - return strings.Trim(string(out), "_") -} - -func unicodeIsLower(r rune) bool { return r >= 'a' && r <= 'z' } -func unicodeIsUpper(r rune) bool { return r >= 'A' && r <= 'Z' } -func unicodeIsDigit(r rune) bool { return r >= '0' && r <= '9' } - -func stripHTML(value string) string { - if value == "" { - return "" - } - re := regexp.MustCompile("<[^>]+>") - cleaned := re.ReplaceAllString(value, " ") - cleaned = strings.Join(strings.Fields(cleaned), " ") - return html.UnescapeString(cleaned) -} - -func slug(value string) string { - out := []rune{} - for _, ch := range value { - if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' { - out = append(out, ch) - } else if ch >= 'A' && ch <= 'Z' { - out = append(out, ch+'a'-'A') - } else { - out = append(out, '_') - } - } - return strings.Trim(string(out), "_") -} - -func fileExists(path string) bool { - _, err := os.Stat(path) - return err == nil -} - -func escapeCode(value string) string { - buf := bytes.NewBuffer(nil) - for _, line := range strings.Split(value, "\n") { - buf.WriteString(html.EscapeString(line)) - buf.WriteString("\n") - } - return buf.String() -} - -// generateIndexMD writes index.md with the list of all processed services. -// The file is written to docsDir so each profile gets its own stand-specific index. -func generateIndexMD(docsDir string, specs []ServiceSpec) { - var b bytes.Buffer - b.WriteString("# Ресурсы провайдера\n\n") - b.WriteString("| ID | Ресурс | Описание |\n") - b.WriteString("|-----|--------|----------|\n") - for _, spec := range specs { - resName := "nubes_" + spec.Name - link := fmt.Sprintf("[%s](%s.md)", resName, spec.Name) - descr := strings.ReplaceAll(spec.ServiceDisplayName, "|", "\\|") - b.WriteString(fmt.Sprintf("| %d | %s | %s |\n", spec.ServiceID, link, descr)) - } - outPath := filepath.Join(docsDir, "index.md") - if err := os.WriteFile(outPath, b.Bytes(), 0o644); err != nil { - fmt.Fprintf(os.Stderr, "Warning: failed to write index.md: %v\n", err) - } -} - -// capitalize делает первую букву строки заглавной. -func capitalize(s string) string { - if s == "" { - return s - } - return strings.ToUpper(s[:1]) + s[1:] -} - -// findSubresourceParams возвращает параметры операции subresource по названию и action. -func findSubresourceParams(ops []OperationSpec, srName, action string) []ParamSpec { - for _, op := range ops { - if op.Kind == "subresource" && op.Subresource == srName && op.Action == action { - return op.Params - } - } - return nil -} - -// buildSubresourcePage генерирует страницу документации для subresource (nubes_{service}_{subresource}). -func buildSubresourcePage(spec ServiceSpec, srName string, nav string) string { - var b strings.Builder - srResourceName := fmt.Sprintf("nubes_%s_%s", spec.Name, slug(srName)) - b.WriteString(fmt.Sprintf("# Resource %s\n\n", srResourceName)) - b.WriteString(fmt.Sprintf("Service: `nubes_%s` (ID: %d)\n\n", spec.Name, spec.ServiceID)) - b.WriteString(nav + "\n\n") - - // Create params - createParams := findSubresourceParams(spec.Operations, srName, "create") - b.WriteString("## Create params\n\n") - if len(createParams) > 0 { - req, def := splitParams(createParams) - b.WriteString("**Обязательные параметры**\n\n") - b.WriteString(renderParamTable(req, true, true)) - if len(def) > 0 { - b.WriteString("\n**Параметры со значением по умолчанию**\n\n") - b.WriteString(renderParamTable(def, false, false)) - } - } else { - b.WriteString("None.\n") - } - - // Modify params (если есть) - modifyParams := findSubresourceParams(spec.Operations, srName, "modify") - if len(modifyParams) > 0 { - b.WriteString("\n## Modify params\n\n") - b.WriteString(renderModifyTable(modifyParams)) - } - - // Delete params - deleteParams := findSubresourceParams(spec.Operations, srName, "delete") - b.WriteString("\n## Delete params\n\n") - if len(deleteParams) > 0 { - b.WriteString(renderParamTable(deleteParams, true, true)) - } else { - b.WriteString("None.\n") - } - - return b.String() -} - -// buildSubresourceExamplePage генерирует страницу с HCL-примером для subresource. -func buildSubresourceExamplePage(spec ServiceSpec, srName string, nav string, version string, apiEndpoint string) string { - var b strings.Builder - srResourceName := fmt.Sprintf("nubes_%s_%s", spec.Name, slug(srName)) - b.WriteString(fmt.Sprintf("# Resource %s — Example\n\n", srResourceName)) - b.WriteString(fmt.Sprintf("Service: `nubes_%s` (ID: %d)\n\n", spec.Name, spec.ServiceID)) - b.WriteString(nav + "\n\n") - b.WriteString(fmt.Sprintf("## Copy-ready manifest (`%s_%s_main.tf`)\n\n", spec.Name, slug(srName))) - b.WriteString("```hcl\n") - - // terraform block - b.WriteString("terraform {\n") - b.WriteString(" required_providers {\n") - b.WriteString(" nubes = {\n") - b.WriteString(" source = \"terra.k8c.ru/nubes/nubes\"\n") - b.WriteString(fmt.Sprintf(" version = \"%s\"\n", version)) - b.WriteString(" }\n") - b.WriteString(" }\n") - b.WriteString("}\n\n") - - b.WriteString("provider \"nubes\" {\n") - b.WriteString(" api_token = \"token_***\"\n") - b.WriteString(fmt.Sprintf(" api_endpoint = \"%s\"\n", apiEndpoint)) - b.WriteString("}\n\n") - - // Основной ресурс (родитель) — ссылка - b.WriteString(fmt.Sprintf("# Родительский ресурс — nubes_%s должен быть создан заранее\n", spec.Name)) - b.WriteString(fmt.Sprintf("# resource \"nubes_%s\" \"baza\" { ... }\n\n", spec.Name)) - - // Subresource - b.WriteString(fmt.Sprintf("resource \"%s\" \"example\" {\n", srResourceName)) - b.WriteString(fmt.Sprintf(" %s_id = nubes_%s.baza.id\n", spec.Name, spec.Name)) - - createParams := findSubresourceParams(spec.Operations, srName, "create") - req, def := splitParams(createParams) - for _, p := range req { - b.WriteString(formatParamLine(p, " ", true)) - } - if len(def) > 0 { - b.WriteString("\n # Параметры со значением по умолчанию\n") - for _, p := range def { - b.WriteString(formatParamLine(p, " ", false)) - } - } - - b.WriteString("}\n") - b.WriteString("```\n") - return b.String() -} diff --git a/TOOLS/ops-generator/generate_ops_docs.go b/TOOLS/ops-generator/generate_ops_docs.go deleted file mode 100644 index 81d00e3..0000000 --- a/TOOLS/ops-generator/generate_ops_docs.go +++ /dev/null @@ -1,289 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/fs" - "os" - "path/filepath" - "sort" - "strings" - - "gopkg.in/yaml.v3" -) - -// ops_docs_gen: builds per-service operations docs from resources_ops_yaml. -// -// Env: -// - NUBES_OPS_YAML_DIR (default: /resources_ops_yaml) -// - NUBES_OPS_DOCS_DIR (default: /../docs/30_registry/resources/operations) - -func main() { - repoRoot, err := findRepoRoot() - if err != nil { - panic(err) - } - - yamlDir := strings.TrimSpace(os.Getenv("NUBES_OPS_YAML_DIR")) - if yamlDir == "" { - yamlDir = filepath.Join(repoRoot, "resources_ops_yaml") - } - - docsDir := strings.TrimSpace(os.Getenv("NUBES_OPS_DOCS_DIR")) - if docsDir == "" { - root := filepath.Dir(repoRoot) - docsDir = filepath.Join(root, "docs", "30_registry", "resources", "operations") - } - - if err := os.MkdirAll(docsDir, 0o755); err != nil { - panic(err) - } - - entries := []docEntry{} - - err = filepath.WalkDir(yamlDir, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() || !strings.HasSuffix(d.Name(), ".yaml") { - return nil - } - b, err := os.ReadFile(path) - if err != nil { - return err - } - var spec ServiceOpsSpec - if err := yaml.Unmarshal(b, &spec); err != nil { - return err - } - if spec.ServiceID == 0 || spec.Name == "" { - return nil - } - - fileName := fmt.Sprintf("%d_%s.md", spec.ServiceID, sanitizeName(spec.Name)) - outPath := filepath.Join(docsDir, fileName) - if err := writeServiceDoc(outPath, spec); err != nil { - return err - } - - entries = append(entries, docEntry{ - ServiceID: spec.ServiceID, - Name: spec.Name, - File: fileName, - Title: serviceTitle(spec), - OpCount: len(spec.Operations), - }) - return nil - }) - if err != nil { - panic(err) - } - - sort.Slice(entries, func(i, j int) bool { return entries[i].ServiceID < entries[j].ServiceID }) - if err := writeIndex(filepath.Join(docsDir, "index.md"), entries); err != nil { - panic(err) - } - - fmt.Printf("Generated %d operations docs in %s\n", len(entries), docsDir) -} - -type docEntry struct { - ServiceID int - Name string - File string - Title string - OpCount int -} - -func writeServiceDoc(path string, spec ServiceOpsSpec) error { - var b strings.Builder - b.WriteString(fmt.Sprintf("# Operations for nubes_%s\n\n", spec.Name)) - b.WriteString(fmt.Sprintf("Service ID: `%d`\n\n", spec.ServiceID)) - - if title := serviceTitle(spec); title != "" { - b.WriteString(fmt.Sprintf("Service: %s\n\n", title)) - } - - if strings.TrimSpace(spec.ServiceMan) != "" { - b.WriteString("## UI description\n\n") - b.WriteString(spec.ServiceMan) - b.WriteString("\n\n") - } - - b.WriteString("## Operations\n\n") - b.WriteString("Non-CRUD operations are typically executed via an action resource.\n") - b.WriteString("Domain operations (for example create_user, create_database) are best modeled as separate resources.\n\n") - if len(spec.Operations) == 0 { - b.WriteString("No operations found.\n") - return os.WriteFile(path, []byte(b.String()), 0o644) - } - - for _, op := range spec.Operations { - b.WriteString(fmt.Sprintf("- `%s` (id: %d)\n", op.Name, op.ID)) - } - b.WriteString("\n") - - for _, op := range spec.Operations { - b.WriteString(fmt.Sprintf("## Operation: %s\n\n", op.Name)) - b.WriteString(fmt.Sprintf("Operation ID: `%d`\n\n", op.ID)) - if strings.TrimSpace(op.Man) != "" { - b.WriteString(op.Man) - b.WriteString("\n\n") - } - if len(op.Params) == 0 { - b.WriteString("No parameters.\n\n") - continue - } - - b.WriteString("| Code | Type | Required | Default | ID | RefSvcId | ValueList | Func | Regex | Min | Max | Sensitive | DependsOn |\n") - b.WriteString("|---|---|---|---|---|---|---|---|---|---|---|---|---|\n") - for _, p := range op.Params { - b.WriteString(fmt.Sprintf("| `%s` | `%s` | `%t` | `%s` | `%d` | `%s` | `%s` | `%s` | `%s` | `%s` | `%s` | `%t` | `%s` |\n", - esc(p.Code), - esc(p.DataType), - p.Required, - esc(formatValue(p.Default)), - p.ID, - esc(formatRef(p.RefSvcId)), - esc(formatList(p.ValueList)), - esc(p.Func), - esc(p.Regex), - esc(formatValue(p.MinValue)), - esc(formatValue(p.MaxValue)), - p.IsSensitive, - esc(formatValue(p.DependsOn)), - )) - } - b.WriteString("\n") - } - - return os.WriteFile(path, []byte(b.String()), 0o644) -} - -func writeIndex(path string, entries []docEntry) error { - var b strings.Builder - b.WriteString("# Operations by service\n\n") - b.WriteString("Auto-generated list of available operations per service.\n\n") - b.WriteString("Note: non-CRUD operations should be invoked via an action resource,\n") - b.WriteString("and domain operations are best represented as dedicated resources.\n\n") - for _, e := range entries { - b.WriteString(fmt.Sprintf("- %d - nubes_%s (%d ops): [%s](%s)\n", e.ServiceID, e.Name, e.OpCount, e.Title, e.File)) - } - return os.WriteFile(path, []byte(b.String()), 0o644) -} - -func sanitizeName(name string) string { - name = strings.ToLower(strings.TrimSpace(name)) - name = strings.ReplaceAll(name, " ", "_") - name = strings.ReplaceAll(name, "/", "_") - name = strings.ReplaceAll(name, "\\", "_") - name = strings.ReplaceAll(name, ":", "_") - name = strings.ReplaceAll(name, "-", "_") - return name -} - -func serviceTitle(spec ServiceOpsSpec) string { - if strings.TrimSpace(spec.ServiceDisplayName) != "" { - return spec.ServiceDisplayName - } - if strings.TrimSpace(spec.ServiceShortName) != "" { - return spec.ServiceShortName - } - return "" -} - -func formatList(items []string) string { - if len(items) == 0 { - return "" - } - return strings.Join(items, ",") -} - -func formatRef(value *int) string { - if value == nil { - return "" - } - return fmt.Sprintf("%d", *value) -} - -func formatValue(value interface{}) string { - if value == nil { - return "" - } - switch t := value.(type) { - case string: - return t - default: - b, err := json.Marshal(t) - if err != nil { - return fmt.Sprintf("%v", value) - } - return string(b) - } -} - -func esc(value string) string { - return strings.ReplaceAll(value, "|", "\\|") -} - -func findRepoRoot() (string, error) { - wd, err := os.Getwd() - if err != nil { - return "", err - } - current := wd - for i := 0; i < 8; i++ { - candidate := filepath.Join(current, "resources_yaml") - info, err := os.Stat(candidate) - if err == nil && info.IsDir() { - return current, nil - } - parent := filepath.Dir(current) - if parent == current { - break - } - current = parent - } - return "", fmt.Errorf("failed to locate universal_rebuild repo root") -} - -// ===== spec ===== - -type ServiceOpsSpec struct { - Name string `yaml:"name"` - ServiceID int `yaml:"service_id"` - ServiceDisplayName string `yaml:"service_display_name,omitempty"` - ServiceShortName string `yaml:"service_short_name,omitempty"` - ServiceMan string `yaml:"service_man,omitempty"` - Operations []OperationSpec `yaml:"operations"` -} - -type OperationSpec struct { - Name string `yaml:"name"` - ID int `yaml:"id"` - Man string `yaml:"man,omitempty"` - Params []ParamSpec `yaml:"params"` -} - -type ParamSpec struct { - ID int `yaml:"id"` - Code string `yaml:"code"` - DataType string `yaml:"data_type,omitempty"` - Required bool `yaml:"required"` - Default interface{} `yaml:"default,omitempty"` - ValueList []string `yaml:"value_list,omitempty"` - RefSvcId *int `yaml:"ref_svc_id,omitempty"` - Func string `yaml:"func,omitempty"` - Regex string `yaml:"regex,omitempty"` - UniqueScope string `yaml:"unique_scope,omitempty"` - MaxLength *int `yaml:"maxlength,omitempty"` - MinLength *int `yaml:"minlength,omitempty"` - MaxValue interface{} `yaml:"maxvalue,omitempty"` - MinValue interface{} `yaml:"minvalue,omitempty"` - Descr string `yaml:"descr,omitempty"` - Man string `yaml:"man,omitempty"` - Sort *int `yaml:"sort,omitempty"` - DependsOn interface{} `yaml:"depends_on,omitempty"` - IsModifiable *bool `yaml:"is_modifiable,omitempty"` - IsSensitive bool `yaml:"is_sensitive,omitempty"` -} diff --git a/TOOLS/ops-generator/internal/docs/docs.go b/TOOLS/ops-generator/internal/docs/docs.go new file mode 100644 index 0000000..63b8fd8 --- /dev/null +++ b/TOOLS/ops-generator/internal/docs/docs.go @@ -0,0 +1,159 @@ +// Package docs — генерация ops-документации. +package docs + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "ops-generator/internal/types" +) + +// Entry — запись в индексе документации. +type Entry struct { + ServiceID int + Name string + File string + Title string + OpCount int +} + +// ServiceDoc генерирует Markdown-документацию операций сервиса. +func ServiceDoc(path string, spec types.ServiceOpsSpec) error { + var b strings.Builder + b.WriteString(fmt.Sprintf("# Operations for nubes_%s\n\n", spec.Name)) + b.WriteString(fmt.Sprintf("Service ID: `%d`\n\n", spec.ServiceID)) + + if t := Title(spec); t != "" { + b.WriteString(fmt.Sprintf("Service: %s\n\n", t)) + } + + if strings.TrimSpace(spec.ServiceMan) != "" { + b.WriteString("## UI description\n\n") + b.WriteString(spec.ServiceMan) + b.WriteString("\n\n") + } + + b.WriteString("## Operations\n\n") + b.WriteString("Non-CRUD operations are typically executed via an action resource.\n") + b.WriteString("Domain operations (for example create_user, create_database) are best modeled as separate resources.\n\n") + if len(spec.Operations) == 0 { + b.WriteString("No operations found.\n") + return os.WriteFile(path, []byte(b.String()), 0o644) + } + + for _, op := range spec.Operations { + b.WriteString(fmt.Sprintf("- `%s` (id: %d)\n", op.Name, op.ID)) + } + b.WriteString("\n") + + for _, op := range spec.Operations { + b.WriteString(fmt.Sprintf("## Operation: %s\n\n", op.Name)) + b.WriteString(fmt.Sprintf("Operation ID: `%d`\n\n", op.ID)) + if strings.TrimSpace(op.Man) != "" { + b.WriteString(op.Man) + b.WriteString("\n\n") + } + if len(op.Params) == 0 { + b.WriteString("No parameters.\n\n") + continue + } + + b.WriteString("| Code | Type | Required | Default | ID | RefSvcId | ValueList | Func | Regex | Min | Max | Sensitive | DependsOn |\n") + b.WriteString("|---|---|---|---|---|---|---|---|---|---|---|---|---|\n") + for _, p := range op.Params { + b.WriteString(fmt.Sprintf("| `%s` | `%s` | `%t` | `%s` | `%d` | `%s` | `%s` | `%s` | `%s` | `%s` | `%s` | `%t` | `%s` |\n", + Esc(p.Code), + Esc(p.DataType), + p.Required, + Esc(Value(p.Default)), + p.ID, + Esc(Ref(p.RefSvcId)), + Esc(List(p.ValueList)), + Esc(p.Func), + Esc(p.Regex), + Esc(Value(p.MinValue)), + Esc(Value(p.MaxValue)), + p.IsSensitive, + Esc(Value(p.DependsOn)), + )) + } + b.WriteString("\n") + } + + return os.WriteFile(path, []byte(b.String()), 0o644) +} + +// Index генерирует index.md со списком всех операций. +func Index(path string, entries []Entry) error { + var b strings.Builder + b.WriteString("# Operations by service\n\n") + b.WriteString("Auto-generated list of available operations per service.\n\n") + b.WriteString("Note: non-CRUD operations should be invoked via an action resource,\n") + b.WriteString("and domain operations are best represented as dedicated resources.\n\n") + for _, e := range entries { + b.WriteString(fmt.Sprintf("- %d - nubes_%s (%d ops): [%s](%s)\n", e.ServiceID, e.Name, e.OpCount, e.Title, e.File)) + } + return os.WriteFile(path, []byte(b.String()), 0o644) +} + +// Name нормализует имя файла. +func Name(name string) string { + name = strings.ToLower(strings.TrimSpace(name)) + name = strings.ReplaceAll(name, " ", "_") + name = strings.ReplaceAll(name, "/", "_") + name = strings.ReplaceAll(name, "\\", "_") + name = strings.ReplaceAll(name, ":", "_") + name = strings.ReplaceAll(name, "-", "_") + return name +} + +// Title возвращает отображаемое имя сервиса. +func Title(spec types.ServiceOpsSpec) string { + if strings.TrimSpace(spec.ServiceDisplayName) != "" { + return spec.ServiceDisplayName + } + if strings.TrimSpace(spec.ServiceShortName) != "" { + return spec.ServiceShortName + } + return "" +} + +// List форматирует список значений. +func List(items []string) string { + if len(items) == 0 { + return "" + } + return strings.Join(items, ",") +} + +// Ref форматирует RefSvcId. +func Ref(value *int) string { + if value == nil { + return "" + } + return fmt.Sprintf("%d", *value) +} + +// Value форматирует значение параметра. +func Value(value interface{}) string { + if value == nil { + return "" + } + switch t := value.(type) { + case string: + return t + default: + b, err := json.Marshal(t) + if err != nil { + return fmt.Sprintf("%v", value) + } + return string(b) + } +} + +// Esc экранирует pipe-символы для Markdown-таблиц. +func Esc(value string) string { + return strings.ReplaceAll(value, "|", "\\|") +} diff --git a/TOOLS/ops-generator/internal/types/types.go b/TOOLS/ops-generator/internal/types/types.go new file mode 100644 index 0000000..33803a4 --- /dev/null +++ b/TOOLS/ops-generator/internal/types/types.go @@ -0,0 +1,44 @@ +// Package types — структуры данных для ops-документации. +package types + +// ServiceOpsSpec — YAML-спек операций сервиса. +type ServiceOpsSpec struct { + Name string `yaml:"name"` + ServiceID int `yaml:"service_id"` + ServiceDisplayName string `yaml:"service_display_name,omitempty"` + ServiceShortName string `yaml:"service_short_name,omitempty"` + ServiceMan string `yaml:"service_man,omitempty"` + Operations []OperationSpec `yaml:"operations"` +} + +// OperationSpec — одна операция. +type OperationSpec struct { + Name string `yaml:"name"` + ID int `yaml:"id"` + Man string `yaml:"man,omitempty"` + Params []ParamSpec `yaml:"params"` +} + +// ParamSpec — параметр операции. +type ParamSpec struct { + ID int `yaml:"id"` + Code string `yaml:"code"` + DataType string `yaml:"data_type,omitempty"` + Required bool `yaml:"required"` + Default interface{} `yaml:"default,omitempty"` + ValueList []string `yaml:"value_list,omitempty"` + RefSvcId *int `yaml:"ref_svc_id,omitempty"` + Func string `yaml:"func,omitempty"` + Regex string `yaml:"regex,omitempty"` + UniqueScope string `yaml:"unique_scope,omitempty"` + MaxLength *int `yaml:"maxlength,omitempty"` + MinLength *int `yaml:"minlength,omitempty"` + MaxValue interface{} `yaml:"maxvalue,omitempty"` + MinValue interface{} `yaml:"minvalue,omitempty"` + Descr string `yaml:"descr,omitempty"` + Man string `yaml:"man,omitempty"` + Sort *int `yaml:"sort,omitempty"` + DependsOn interface{} `yaml:"depends_on,omitempty"` + IsModifiable *bool `yaml:"is_modifiable,omitempty"` + IsSensitive bool `yaml:"is_sensitive,omitempty"` +} diff --git a/TOOLS/ops-generator/main.go b/TOOLS/ops-generator/main.go new file mode 100644 index 0000000..afce4d4 --- /dev/null +++ b/TOOLS/ops-generator/main.go @@ -0,0 +1,106 @@ +// ops-generator — генератор per-service operations документации. +package main + +import ( + "fmt" + "io/fs" + "os" + "path/filepath" + "sort" + "strings" + + "gopkg.in/yaml.v3" + + "ops-generator/internal/docs" + "ops-generator/internal/types" +) + +func main() { + repoRoot, err := findRepoRoot() + if err != nil { + panic(err) + } + + yamlDir := strings.TrimSpace(os.Getenv("NUBES_OPS_YAML_DIR")) + if yamlDir == "" { + yamlDir = filepath.Join(repoRoot, "resources_ops_yaml") + } + + docsDir := strings.TrimSpace(os.Getenv("NUBES_OPS_DOCS_DIR")) + if docsDir == "" { + root := filepath.Dir(repoRoot) + docsDir = filepath.Join(root, "docs", "30_registry", "resources", "operations") + } + + if err := os.MkdirAll(docsDir, 0o755); err != nil { + panic(err) + } + + entries := []docs.Entry{} + + err = filepath.WalkDir(yamlDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || !strings.HasSuffix(d.Name(), ".yaml") { + return nil + } + b, err := os.ReadFile(path) + if err != nil { + return err + } + var spec types.ServiceOpsSpec + if err := yaml.Unmarshal(b, &spec); err != nil { + return err + } + if spec.ServiceID == 0 || spec.Name == "" { + return nil + } + + fileName := fmt.Sprintf("%d_%s.md", spec.ServiceID, docs.Name(spec.Name)) + outPath := filepath.Join(docsDir, fileName) + if err := docs.ServiceDoc(outPath, spec); err != nil { + return err + } + + entries = append(entries, docs.Entry{ + ServiceID: spec.ServiceID, + Name: spec.Name, + File: fileName, + Title: docs.Title(spec), + OpCount: len(spec.Operations), + }) + return nil + }) + if err != nil { + panic(err) + } + + sort.Slice(entries, func(i, j int) bool { return entries[i].ServiceID < entries[j].ServiceID }) + if err := docs.Index(filepath.Join(docsDir, "index.md"), entries); err != nil { + panic(err) + } + + fmt.Printf("Generated %d operations docs in %s\n", len(entries), docsDir) +} + +func findRepoRoot() (string, error) { + wd, err := os.Getwd() + if err != nil { + return "", err + } + current := wd + for i := 0; i < 8; i++ { + candidate := filepath.Join(current, "resources_yaml") + info, err := os.Stat(candidate) + if err == nil && info.IsDir() { + return current, nil + } + parent := filepath.Dir(current) + if parent == current { + break + } + current = parent + } + return "", fmt.Errorf("failed to locate provider repo root") +} diff --git a/TOOLS/resource-generator/helpers/helpers.go b/TOOLS/resource-generator/internal/helpers/helpers.go similarity index 99% rename from TOOLS/resource-generator/helpers/helpers.go rename to TOOLS/resource-generator/internal/helpers/helpers.go index 0a12819..a1dac43 100644 --- a/TOOLS/resource-generator/helpers/helpers.go +++ b/TOOLS/resource-generator/internal/helpers/helpers.go @@ -8,7 +8,7 @@ import ( "strings" "unicode" - "resource-generator/types" + "resource-generator/internal/types" ) // ToCamel конвертирует snake_case → CamelCase. diff --git a/TOOLS/resource-generator/loader/loader.go b/TOOLS/resource-generator/internal/loader/loader.go similarity index 99% rename from TOOLS/resource-generator/loader/loader.go rename to TOOLS/resource-generator/internal/loader/loader.go index 771b64c..d4a8a2b 100644 --- a/TOOLS/resource-generator/loader/loader.go +++ b/TOOLS/resource-generator/internal/loader/loader.go @@ -11,8 +11,8 @@ import ( "gopkg.in/yaml.v3" - "resource-generator/params" - "resource-generator/types" + "resource-generator/internal/params" + "resource-generator/internal/types" ) // LoadSpecs загружает все YAML-спеки из директории и строит GenResource/GenSubresource/GenAction. diff --git a/TOOLS/resource-generator/params/params.go b/TOOLS/resource-generator/internal/params/params.go similarity index 99% rename from TOOLS/resource-generator/params/params.go rename to TOOLS/resource-generator/internal/params/params.go index 4974bfb..09afe16 100644 --- a/TOOLS/resource-generator/params/params.go +++ b/TOOLS/resource-generator/internal/params/params.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - "resource-generator/types" + "resource-generator/internal/types" ) // Merge объединяет несколько наборов параметров, убирая дубликаты по Code. diff --git a/TOOLS/resource-generator/templates/templates.go b/TOOLS/resource-generator/internal/templates/templates.go similarity index 100% rename from TOOLS/resource-generator/templates/templates.go rename to TOOLS/resource-generator/internal/templates/templates.go diff --git a/TOOLS/resource-generator/types/types.go b/TOOLS/resource-generator/internal/types/types.go similarity index 100% rename from TOOLS/resource-generator/types/types.go rename to TOOLS/resource-generator/internal/types/types.go diff --git a/TOOLS/resource-generator/writers/writers.go b/TOOLS/resource-generator/internal/writers/writers.go similarity index 97% rename from TOOLS/resource-generator/writers/writers.go rename to TOOLS/resource-generator/internal/writers/writers.go index 21713c5..3b2dc4d 100644 --- a/TOOLS/resource-generator/writers/writers.go +++ b/TOOLS/resource-generator/internal/writers/writers.go @@ -8,9 +8,9 @@ import ( "path/filepath" "text/template" - "resource-generator/helpers" - "resource-generator/templates" - "resource-generator/types" + "resource-generator/internal/helpers" + "resource-generator/internal/templates" + "resource-generator/internal/types" ) // WriteInstanceResource генерирует nubes_{service} (CRUD инстанса). diff --git a/TOOLS/resource-generator/main.go b/TOOLS/resource-generator/main.go index e247b37..b3aad42 100644 --- a/TOOLS/resource-generator/main.go +++ b/TOOLS/resource-generator/main.go @@ -20,8 +20,8 @@ import ( "path/filepath" "strings" - "resource-generator/loader" - "resource-generator/writers" + "resource-generator/internal/loader" + "resource-generator/internal/writers" ) func main() { diff --git a/TOOLS/yaml-generator/client/client.go b/TOOLS/yaml-generator/internal/client/client.go similarity index 98% rename from TOOLS/yaml-generator/client/client.go rename to TOOLS/yaml-generator/internal/client/client.go index a505886..86276b5 100644 --- a/TOOLS/yaml-generator/client/client.go +++ b/TOOLS/yaml-generator/internal/client/client.go @@ -10,8 +10,8 @@ import ( "strings" "time" - "yaml-generator/normalize" - "yaml-generator/types" + "yaml-generator/internal/normalize" + "yaml-generator/internal/types" ) // Client — HTTP-клиент для Nubes API. diff --git a/TOOLS/yaml-generator/config/config.go b/TOOLS/yaml-generator/internal/config/config.go similarity index 95% rename from TOOLS/yaml-generator/config/config.go rename to TOOLS/yaml-generator/internal/config/config.go index 8baa04e..a519737 100644 --- a/TOOLS/yaml-generator/config/config.go +++ b/TOOLS/yaml-generator/internal/config/config.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "yaml-generator/normalize" + "yaml-generator/internal/normalize" ) // Config — конфигурация генератора. @@ -170,7 +170,7 @@ func ReadServicesList(path string) ([]ServiceRef, error) { return services, nil } -// FindRepoRoot ищет корень репозитория по директории universal_rebuild. +// FindRepoRoot ищет корень репозитория по директории provider. func FindRepoRoot() (string, error) { wd, err := os.Getwd() if err != nil { @@ -178,7 +178,7 @@ func FindRepoRoot() (string, error) { } current := wd for i := 0; i < 8; i++ { - candidate := filepath.Join(current, "universal_rebuild") + candidate := filepath.Join(current, "provider") info, err := os.Stat(candidate) if err == nil && info.IsDir() { return candidate, nil @@ -189,5 +189,5 @@ func FindRepoRoot() (string, error) { } current = parent } - return "", errors.New("failed to locate universal_rebuild repo root") + return "", errors.New("failed to locate provider repo root") } diff --git a/TOOLS/yaml-generator/normalize/normalize.go b/TOOLS/yaml-generator/internal/normalize/normalize.go similarity index 100% rename from TOOLS/yaml-generator/normalize/normalize.go rename to TOOLS/yaml-generator/internal/normalize/normalize.go diff --git a/TOOLS/yaml-generator/spec/spec.go b/TOOLS/yaml-generator/internal/spec/spec.go similarity index 97% rename from TOOLS/yaml-generator/spec/spec.go rename to TOOLS/yaml-generator/internal/spec/spec.go index 193dc7d..8961bdc 100644 --- a/TOOLS/yaml-generator/spec/spec.go +++ b/TOOLS/yaml-generator/internal/spec/spec.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "yaml-generator/types" + "yaml-generator/internal/types" ) // DefaultOutputParams возвращает стандартный набор выходных параметров. diff --git a/TOOLS/yaml-generator/types/types.go b/TOOLS/yaml-generator/internal/types/types.go similarity index 100% rename from TOOLS/yaml-generator/types/types.go rename to TOOLS/yaml-generator/internal/types/types.go diff --git a/TOOLS/yaml-generator/main.go b/TOOLS/yaml-generator/main.go index f254633..bde56a9 100644 --- a/TOOLS/yaml-generator/main.go +++ b/TOOLS/yaml-generator/main.go @@ -12,11 +12,11 @@ import ( "gopkg.in/yaml.v3" - "yaml-generator/client" - "yaml-generator/config" - "yaml-generator/normalize" - "yaml-generator/spec" - "yaml-generator/types" + "yaml-generator/internal/client" + "yaml-generator/internal/config" + "yaml-generator/internal/normalize" + "yaml-generator/internal/spec" + "yaml-generator/internal/types" ) func main() { diff --git a/devops/01_generate_yamls.sh b/devops/01_generate_yamls.sh index 7712569..a41d114 100755 --- a/devops/01_generate_yamls.sh +++ b/devops/01_generate_yamls.sh @@ -3,7 +3,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_DIR="${ROOT_DIR}/universal_rebuild" +PROVIDER_DIR="${ROOT_DIR}/provider" PROFILE_DIR="${PROFILE_DIR:-}" if [[ "${1:-}" == "--profile" ]]; then @@ -64,7 +64,7 @@ fi # Назначение: # - Берет список сервисов из services_list.txt. # - Для каждого сервиса запрашивает единый spec через API и пишет YAML в -# ${ROOT_DIR}/universal_rebuild/resources_yaml. +# ${ROOT_DIR}/provider/resources_yaml. # - Формат имени файла: ID_имя.yaml (например, 13_s3bucket.yaml). # # Источники данных: diff --git a/devops/02_generate_resources_and_docs.sh b/devops/02_generate_resources_and_docs.sh index f3ded27..a4b8d73 100755 --- a/devops/02_generate_resources_and_docs.sh +++ b/devops/02_generate_resources_and_docs.sh @@ -4,7 +4,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_DIR="${ROOT_DIR}/universal_rebuild" +PROVIDER_DIR="${ROOT_DIR}/provider" DOCS_DIR="${ROOT_DIR}/docs/30_registry/resources" cd "$PROVIDER_DIR" diff --git a/devops/02_generate_resources_and_docs_template.sh b/devops/02_generate_resources_and_docs_template.sh index 777600a..438f4e6 100755 --- a/devops/02_generate_resources_and_docs_template.sh +++ b/devops/02_generate_resources_and_docs_template.sh @@ -6,7 +6,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_DIR="${ROOT_DIR}/universal_rebuild" +PROVIDER_DIR="${ROOT_DIR}/provider" RESOURCES_YAML_DIR="${PROVIDER_DIR}/resources_yaml" DOCS_DIR="${ROOT_DIR}/docs/30_registry/resources" SERVICES_LIST_PATH="${ROOT_DIR}/devops/config/services_list.txt" diff --git a/devops/02_generate_resources_and_docs_template_v2.sh b/devops/02_generate_resources_and_docs_template_v2.sh index 26d19fe..3e86dad 100755 --- a/devops/02_generate_resources_and_docs_template_v2.sh +++ b/devops/02_generate_resources_and_docs_template_v2.sh @@ -6,7 +6,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_DIR="${ROOT_DIR}/universal_rebuild" +PROVIDER_DIR="${ROOT_DIR}/provider" RESOURCES_YAML_DIR="${PROVIDER_DIR}/resources_yaml" DOCS_DIR="${ROOT_DIR}/docs/30_registry/resources" SERVICES_LIST_PATH="${ROOT_DIR}/devops/config/services_list.txt" diff --git a/devops/02_generate_resources_and_docs_v2.sh b/devops/02_generate_resources_and_docs_v2.sh index 2a0c2c6..13a39bc 100755 --- a/devops/02_generate_resources_and_docs_v2.sh +++ b/devops/02_generate_resources_and_docs_v2.sh @@ -6,7 +6,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_DIR="${ROOT_DIR}/universal_rebuild" +PROVIDER_DIR="${ROOT_DIR}/provider" PROFILE_DIR="${PROFILE_DIR:-}" resolve_root_path() { diff --git a/devops/03_build_and_upload_provider.sh b/devops/03_build_and_upload_provider.sh index b8f88b1..3d2df4d 100755 --- a/devops/03_build_and_upload_provider.sh +++ b/devops/03_build_and_upload_provider.sh @@ -3,7 +3,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_MAIN="${ROOT_DIR}/universal_rebuild/main.go" +PROVIDER_MAIN="${ROOT_DIR}/provider/main.go" BUILD_SCRIPT="${ROOT_DIR}/devops/build-provider.sh" PROFILE_DIR="${PROFILE_DIR:-}" @@ -171,7 +171,7 @@ validate_registry_completeness() { TMP_PROVIDER_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_PROVIDER_DIR"' EXIT -cp -a "${ROOT_DIR}/universal_rebuild/." "$TMP_PROVIDER_DIR/" +cp -a "${ROOT_DIR}/provider/." "$TMP_PROVIDER_DIR/" mkdir -p "$TMP_PROVIDER_DIR/resources_yaml" find "$TMP_PROVIDER_DIR/resources_yaml" -maxdepth 1 -type f -name '*.yaml' -delete cp -a "${PROFILE_RESOURCES_YAML_DIR}"/*.yaml "$TMP_PROVIDER_DIR/resources_yaml/" diff --git a/devops/04_build_and_publish_docs.sh b/devops/04_build_and_publish_docs.sh index c992991..68428dc 100755 --- a/devops/04_build_and_publish_docs.sh +++ b/devops/04_build_and_publish_docs.sh @@ -3,7 +3,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_MAIN="${ROOT_DIR}/universal_rebuild/main.go" +PROVIDER_MAIN="${ROOT_DIR}/provider/main.go" PROFILE_DIR="${PROFILE_DIR:-}" normalize_api_endpoint() { @@ -74,7 +74,7 @@ TMP_DOCS_DIR="" if [[ -n "$PROFILE_DIR" ]]; then # ⛔ NEVER merge with docs/ — ONLY generated docs from docs_gen// - DOCS_GEN_DIR="${DOCS_GEN_DIR:-universal_rebuild/docs_gen/test}" + DOCS_GEN_DIR="${DOCS_GEN_DIR:-provider/docs_gen/test}" DOCS_GEN_DIR="$(resolve_root_path "$DOCS_GEN_DIR")" if [[ -d "$DOCS_GEN_DIR" ]]; then export MKDOCS_DOCS_DIR="$DOCS_GEN_DIR" diff --git a/devops/13_generate_yamls_clean.sh b/devops/13_generate_yamls_clean.sh index 125b06f..826d51b 100755 --- a/devops/13_generate_yamls_clean.sh +++ b/devops/13_generate_yamls_clean.sh @@ -3,7 +3,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_DIR="${ROOT_DIR}/universal_rebuild" +PROVIDER_DIR="${ROOT_DIR}/provider" if [[ ! -d "${PROVIDER_DIR}/resources_yaml" ]]; then echo "Error: resources_yaml not found: ${PROVIDER_DIR}/resources_yaml" >&2 diff --git a/devops/build-provider.sh b/devops/build-provider.sh index b9387ee..9099aca 100755 --- a/devops/build-provider.sh +++ b/devops/build-provider.sh @@ -29,7 +29,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="${ROOT_DIR:-$(cd "${SCRIPT_DIR}/.." && pwd)}" -PROVIDER_DIR="${PROVIDER_DIR:-${ROOT_DIR}/universal_rebuild}" +PROVIDER_DIR="${PROVIDER_DIR:-${ROOT_DIR}/provider}" BUILD_DIR="${BUILD_DIR:-${ROOT_DIR}/artifacts/provider_build}" GPG_KEY_FILE="${GPG_KEY_FILE:-${ROOT_DIR}/secrets/private_key.asc}" diff --git a/universal_rebuild/.gitignore b/provider/.gitignore similarity index 100% rename from universal_rebuild/.gitignore rename to provider/.gitignore diff --git a/universal_rebuild/go.mod b/provider/go.mod similarity index 100% rename from universal_rebuild/go.mod rename to provider/go.mod diff --git a/universal_rebuild/go.sum b/provider/go.sum similarity index 100% rename from universal_rebuild/go.sum rename to provider/go.sum diff --git a/universal_rebuild/internal/core/client.go b/provider/internal/core/client.go similarity index 100% rename from universal_rebuild/internal/core/client.go rename to provider/internal/core/client.go diff --git a/universal_rebuild/internal/core/client_test.go b/provider/internal/core/client_test.go similarity index 100% rename from universal_rebuild/internal/core/client_test.go rename to provider/internal/core/client_test.go diff --git a/universal_rebuild/internal/core/instance_outputs.go b/provider/internal/core/instance_outputs.go similarity index 100% rename from universal_rebuild/internal/core/instance_outputs.go rename to provider/internal/core/instance_outputs.go diff --git a/universal_rebuild/internal/core/instance_params.go b/provider/internal/core/instance_params.go similarity index 100% rename from universal_rebuild/internal/core/instance_params.go rename to provider/internal/core/instance_params.go diff --git a/universal_rebuild/internal/core/operation_timeout_override.go b/provider/internal/core/operation_timeout_override.go similarity index 100% rename from universal_rebuild/internal/core/operation_timeout_override.go rename to provider/internal/core/operation_timeout_override.go diff --git a/universal_rebuild/internal/core/operation_timeouts.go b/provider/internal/core/operation_timeouts.go similarity index 100% rename from universal_rebuild/internal/core/operation_timeouts.go rename to provider/internal/core/operation_timeouts.go diff --git a/universal_rebuild/internal/core/refsvc_resolve.go b/provider/internal/core/refsvc_resolve.go similarity index 100% rename from universal_rebuild/internal/core/refsvc_resolve.go rename to provider/internal/core/refsvc_resolve.go diff --git a/universal_rebuild/internal/provider/operation_timeouts.json b/provider/internal/provider/operation_timeouts.json similarity index 100% rename from universal_rebuild/internal/provider/operation_timeouts.json rename to provider/internal/provider/operation_timeouts.json diff --git a/universal_rebuild/internal/provider/operation_timeouts_embed.go b/provider/internal/provider/operation_timeouts_embed.go similarity index 100% rename from universal_rebuild/internal/provider/operation_timeouts_embed.go rename to provider/internal/provider/operation_timeouts_embed.go diff --git a/universal_rebuild/internal/provider/provider.go b/provider/internal/provider/provider.go similarity index 100% rename from universal_rebuild/internal/provider/provider.go rename to provider/internal/provider/provider.go diff --git a/universal_rebuild/internal/resources_core/crud.go b/provider/internal/resources_core/crud.go similarity index 100% rename from universal_rebuild/internal/resources_core/crud.go rename to provider/internal/resources_core/crud.go diff --git a/universal_rebuild/internal/resources_core/crud_test.go b/provider/internal/resources_core/crud_test.go similarity index 100% rename from universal_rebuild/internal/resources_core/crud_test.go rename to provider/internal/resources_core/crud_test.go diff --git a/universal_rebuild/internal/resources_core/domain_collision.go b/provider/internal/resources_core/domain_collision.go similarity index 100% rename from universal_rebuild/internal/resources_core/domain_collision.go rename to provider/internal/resources_core/domain_collision.go diff --git a/universal_rebuild/internal/resources_core/helpers.go b/provider/internal/resources_core/helpers.go similarity index 100% rename from universal_rebuild/internal/resources_core/helpers.go rename to provider/internal/resources_core/helpers.go diff --git a/universal_rebuild/internal/resources_core/instance_state.go b/provider/internal/resources_core/instance_state.go similarity index 100% rename from universal_rebuild/internal/resources_core/instance_state.go rename to provider/internal/resources_core/instance_state.go diff --git a/universal_rebuild/internal/resources_core/json_normalize.go b/provider/internal/resources_core/json_normalize.go similarity index 100% rename from universal_rebuild/internal/resources_core/json_normalize.go rename to provider/internal/resources_core/json_normalize.go diff --git a/universal_rebuild/internal/resources_core/json_planmodifier.go b/provider/internal/resources_core/json_planmodifier.go similarity index 100% rename from universal_rebuild/internal/resources_core/json_planmodifier.go rename to provider/internal/resources_core/json_planmodifier.go diff --git a/universal_rebuild/internal/resources_core/operation_ids.go b/provider/internal/resources_core/operation_ids.go similarity index 100% rename from universal_rebuild/internal/resources_core/operation_ids.go rename to provider/internal/resources_core/operation_ids.go diff --git a/universal_rebuild/internal/resources_core/outputs.go b/provider/internal/resources_core/outputs.go similarity index 100% rename from universal_rebuild/internal/resources_core/outputs.go rename to provider/internal/resources_core/outputs.go diff --git a/universal_rebuild/internal/resources_core/params_compare.go b/provider/internal/resources_core/params_compare.go similarity index 100% rename from universal_rebuild/internal/resources_core/params_compare.go rename to provider/internal/resources_core/params_compare.go diff --git a/universal_rebuild/internal/resources_core/params_mapping.go b/provider/internal/resources_core/params_mapping.go similarity index 100% rename from universal_rebuild/internal/resources_core/params_mapping.go rename to provider/internal/resources_core/params_mapping.go diff --git a/universal_rebuild/internal/resources_core/params_ref_mapping.go b/provider/internal/resources_core/params_ref_mapping.go similarity index 100% rename from universal_rebuild/internal/resources_core/params_ref_mapping.go rename to provider/internal/resources_core/params_ref_mapping.go diff --git a/universal_rebuild/internal/resources_core/params_validation_mapping.go b/provider/internal/resources_core/params_validation_mapping.go similarity index 100% rename from universal_rebuild/internal/resources_core/params_validation_mapping.go rename to provider/internal/resources_core/params_validation_mapping.go diff --git a/universal_rebuild/internal/resources_core/ref_validation.go b/provider/internal/resources_core/ref_validation.go similarity index 100% rename from universal_rebuild/internal/resources_core/ref_validation.go rename to provider/internal/resources_core/ref_validation.go diff --git a/universal_rebuild/internal/resources_core/required_params.go b/provider/internal/resources_core/required_params.go similarity index 100% rename from universal_rebuild/internal/resources_core/required_params.go rename to provider/internal/resources_core/required_params.go diff --git a/universal_rebuild/internal/resources_core/required_params_compare.go b/provider/internal/resources_core/required_params_compare.go similarity index 100% rename from universal_rebuild/internal/resources_core/required_params_compare.go rename to provider/internal/resources_core/required_params_compare.go diff --git a/universal_rebuild/internal/resources_core/resource_diagnostics.go b/provider/internal/resources_core/resource_diagnostics.go similarity index 100% rename from universal_rebuild/internal/resources_core/resource_diagnostics.go rename to provider/internal/resources_core/resource_diagnostics.go diff --git a/universal_rebuild/internal/resources_core/resource_diagnostics_required.go b/provider/internal/resources_core/resource_diagnostics_required.go similarity index 100% rename from universal_rebuild/internal/resources_core/resource_diagnostics_required.go rename to provider/internal/resources_core/resource_diagnostics_required.go diff --git a/universal_rebuild/internal/resources_core/service_operation_resource.go b/provider/internal/resources_core/service_operation_resource.go similarity index 100% rename from universal_rebuild/internal/resources_core/service_operation_resource.go rename to provider/internal/resources_core/service_operation_resource.go diff --git a/universal_rebuild/internal/resources_core/state_refresh.go b/provider/internal/resources_core/state_refresh.go similarity index 100% rename from universal_rebuild/internal/resources_core/state_refresh.go rename to provider/internal/resources_core/state_refresh.go diff --git a/universal_rebuild/internal/resources_core/subresource_guard.go b/provider/internal/resources_core/subresource_guard.go similarity index 100% rename from universal_rebuild/internal/resources_core/subresource_guard.go rename to provider/internal/resources_core/subresource_guard.go diff --git a/universal_rebuild/internal/resources_core/uuid_planmodifier.go b/provider/internal/resources_core/uuid_planmodifier.go similarity index 100% rename from universal_rebuild/internal/resources_core/uuid_planmodifier.go rename to provider/internal/resources_core/uuid_planmodifier.go diff --git a/universal_rebuild/internal/resources_gen/100_openwhisk_resource.go b/provider/internal/resources_gen/100_openwhisk_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/100_openwhisk_resource.go rename to provider/internal/resources_gen/100_openwhisk_resource.go diff --git a/universal_rebuild/internal/resources_gen/110_dnszone_resource.go b/provider/internal/resources_gen/110_dnszone_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/110_dnszone_resource.go rename to provider/internal/resources_gen/110_dnszone_resource.go diff --git a/universal_rebuild/internal/resources_gen/111_dnsrecord_reconcile_action.go b/provider/internal/resources_gen/111_dnsrecord_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/111_dnsrecord_reconcile_action.go rename to provider/internal/resources_gen/111_dnsrecord_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/111_dnsrecord_resource.go b/provider/internal/resources_gen/111_dnsrecord_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/111_dnsrecord_resource.go rename to provider/internal/resources_gen/111_dnsrecord_resource.go diff --git a/universal_rebuild/internal/resources_gen/112_tenant_resource.go b/provider/internal/resources_gen/112_tenant_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/112_tenant_resource.go rename to provider/internal/resources_gen/112_tenant_resource.go diff --git a/universal_rebuild/internal/resources_gen/113_vc_complex_resource.go b/provider/internal/resources_gen/113_vc_complex_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/113_vc_complex_resource.go rename to provider/internal/resources_gen/113_vc_complex_resource.go diff --git a/universal_rebuild/internal/resources_gen/114_gitea_complex_resource.go b/provider/internal/resources_gen/114_gitea_complex_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/114_gitea_complex_resource.go rename to provider/internal/resources_gen/114_gitea_complex_resource.go diff --git a/universal_rebuild/internal/resources_gen/115_mariadb_database_resource.go b/provider/internal/resources_gen/115_mariadb_database_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/115_mariadb_database_resource.go rename to provider/internal/resources_gen/115_mariadb_database_resource.go diff --git a/universal_rebuild/internal/resources_gen/115_mariadb_reconcile_action.go b/provider/internal/resources_gen/115_mariadb_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/115_mariadb_reconcile_action.go rename to provider/internal/resources_gen/115_mariadb_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/115_mariadb_resource.go b/provider/internal/resources_gen/115_mariadb_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/115_mariadb_resource.go rename to provider/internal/resources_gen/115_mariadb_resource.go diff --git a/universal_rebuild/internal/resources_gen/115_mariadb_user_resource.go b/provider/internal/resources_gen/115_mariadb_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/115_mariadb_user_resource.go rename to provider/internal/resources_gen/115_mariadb_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/116_kafka_resource.go b/provider/internal/resources_gen/116_kafka_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/116_kafka_resource.go rename to provider/internal/resources_gen/116_kafka_resource.go diff --git a/universal_rebuild/internal/resources_gen/116_kafka_topic_resource.go b/provider/internal/resources_gen/116_kafka_topic_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/116_kafka_topic_resource.go rename to provider/internal/resources_gen/116_kafka_topic_resource.go diff --git a/universal_rebuild/internal/resources_gen/116_kafka_user_resource.go b/provider/internal/resources_gen/116_kafka_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/116_kafka_user_resource.go rename to provider/internal/resources_gen/116_kafka_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/117_nifi_resource.go b/provider/internal/resources_gen/117_nifi_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/117_nifi_resource.go rename to provider/internal/resources_gen/117_nifi_resource.go diff --git a/universal_rebuild/internal/resources_gen/119_akhq_resource.go b/provider/internal/resources_gen/119_akhq_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/119_akhq_resource.go rename to provider/internal/resources_gen/119_akhq_resource.go diff --git a/universal_rebuild/internal/resources_gen/120_clickhouse_database_resource.go b/provider/internal/resources_gen/120_clickhouse_database_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/120_clickhouse_database_resource.go rename to provider/internal/resources_gen/120_clickhouse_database_resource.go diff --git a/universal_rebuild/internal/resources_gen/120_clickhouse_reconcile_action.go b/provider/internal/resources_gen/120_clickhouse_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/120_clickhouse_reconcile_action.go rename to provider/internal/resources_gen/120_clickhouse_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/120_clickhouse_resource.go b/provider/internal/resources_gen/120_clickhouse_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/120_clickhouse_resource.go rename to provider/internal/resources_gen/120_clickhouse_resource.go diff --git a/universal_rebuild/internal/resources_gen/120_clickhouse_user_resource.go b/provider/internal/resources_gen/120_clickhouse_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/120_clickhouse_user_resource.go rename to provider/internal/resources_gen/120_clickhouse_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/12_s3_reconcile_action.go b/provider/internal/resources_gen/12_s3_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/12_s3_reconcile_action.go rename to provider/internal/resources_gen/12_s3_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/12_s3_resource.go b/provider/internal/resources_gen/12_s3_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/12_s3_resource.go rename to provider/internal/resources_gen/12_s3_resource.go diff --git a/universal_rebuild/internal/resources_gen/12_s3_sub_user_resource.go b/provider/internal/resources_gen/12_s3_sub_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/12_s3_sub_user_resource.go rename to provider/internal/resources_gen/12_s3_sub_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/13_s3bucket_resource.go b/provider/internal/resources_gen/13_s3bucket_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/13_s3bucket_resource.go rename to provider/internal/resources_gen/13_s3bucket_resource.go diff --git a/universal_rebuild/internal/resources_gen/149_valo_tenant_resource.go b/provider/internal/resources_gen/149_valo_tenant_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/149_valo_tenant_resource.go rename to provider/internal/resources_gen/149_valo_tenant_resource.go diff --git a/universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_reconcile_action.go b/provider/internal/resources_gen/150_k8s_sthutrval_cluster_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_reconcile_action.go rename to provider/internal/resources_gen/150_k8s_sthutrval_cluster_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go b/provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go rename to provider/internal/resources_gen/150_k8s_sthutrval_cluster_resource.go diff --git a/universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_update_secrets_action.go b/provider/internal/resources_gen/150_k8s_sthutrval_cluster_update_secrets_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_update_secrets_action.go rename to provider/internal/resources_gen/150_k8s_sthutrval_cluster_update_secrets_action.go diff --git a/universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go b/provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go rename to provider/internal/resources_gen/150_k8s_sthutrval_cluster_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/19_vc_org_reconcile_action.go b/provider/internal/resources_gen/19_vc_org_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/19_vc_org_reconcile_action.go rename to provider/internal/resources_gen/19_vc_org_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/19_vc_org_resource.go b/provider/internal/resources_gen/19_vc_org_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/19_vc_org_resource.go rename to provider/internal/resources_gen/19_vc_org_resource.go diff --git a/universal_rebuild/internal/resources_gen/19_vc_org_user_resource.go b/provider/internal/resources_gen/19_vc_org_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/19_vc_org_user_resource.go rename to provider/internal/resources_gen/19_vc_org_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/1_dummy_reconcile_action.go b/provider/internal/resources_gen/1_dummy_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/1_dummy_reconcile_action.go rename to provider/internal/resources_gen/1_dummy_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/1_dummy_redeploy_action.go b/provider/internal/resources_gen/1_dummy_redeploy_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/1_dummy_redeploy_action.go rename to provider/internal/resources_gen/1_dummy_redeploy_action.go diff --git a/universal_rebuild/internal/resources_gen/1_dummy_resource.go b/provider/internal/resources_gen/1_dummy_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/1_dummy_resource.go rename to provider/internal/resources_gen/1_dummy_resource.go diff --git a/universal_rebuild/internal/resources_gen/20_vc_org_saas_resource.go b/provider/internal/resources_gen/20_vc_org_saas_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/20_vc_org_saas_resource.go rename to provider/internal/resources_gen/20_vc_org_saas_resource.go diff --git a/universal_rebuild/internal/resources_gen/21_vc_vdc_reconcile_action.go b/provider/internal/resources_gen/21_vc_vdc_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/21_vc_vdc_reconcile_action.go rename to provider/internal/resources_gen/21_vc_vdc_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/21_vc_vdc_resource.go b/provider/internal/resources_gen/21_vc_vdc_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/21_vc_vdc_resource.go rename to provider/internal/resources_gen/21_vc_vdc_resource.go diff --git a/universal_rebuild/internal/resources_gen/22_vc_nsxt_reconcile_action.go b/provider/internal/resources_gen/22_vc_nsxt_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/22_vc_nsxt_reconcile_action.go rename to provider/internal/resources_gen/22_vc_nsxt_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/22_vc_nsxt_resource.go b/provider/internal/resources_gen/22_vc_nsxt_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/22_vc_nsxt_resource.go rename to provider/internal/resources_gen/22_vc_nsxt_resource.go diff --git a/universal_rebuild/internal/resources_gen/23_vc_vm_resource.go b/provider/internal/resources_gen/23_vc_vm_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/23_vc_vm_resource.go rename to provider/internal/resources_gen/23_vc_vm_resource.go diff --git a/universal_rebuild/internal/resources_gen/25_vcexternalip_resource.go b/provider/internal/resources_gen/25_vcexternalip_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/25_vcexternalip_resource.go rename to provider/internal/resources_gen/25_vcexternalip_resource.go diff --git a/universal_rebuild/internal/resources_gen/26_vapp_reconcile_action.go b/provider/internal/resources_gen/26_vapp_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/26_vapp_reconcile_action.go rename to provider/internal/resources_gen/26_vapp_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/26_vapp_resource.go b/provider/internal/resources_gen/26_vapp_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/26_vapp_resource.go rename to provider/internal/resources_gen/26_vapp_resource.go diff --git a/universal_rebuild/internal/resources_gen/27_vc_vm_v2_resource.go b/provider/internal/resources_gen/27_vc_vm_v2_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/27_vc_vm_v2_resource.go rename to provider/internal/resources_gen/27_vc_vm_v2_resource.go diff --git a/universal_rebuild/internal/resources_gen/28_vc_vm_v3_reconcile_action.go b/provider/internal/resources_gen/28_vc_vm_v3_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/28_vc_vm_v3_reconcile_action.go rename to provider/internal/resources_gen/28_vc_vm_v3_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/28_vc_vm_v3_redeploy_action.go b/provider/internal/resources_gen/28_vc_vm_v3_redeploy_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/28_vc_vm_v3_redeploy_action.go rename to provider/internal/resources_gen/28_vc_vm_v3_redeploy_action.go diff --git a/universal_rebuild/internal/resources_gen/28_vc_vm_v3_resource.go b/provider/internal/resources_gen/28_vc_vm_v3_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/28_vc_vm_v3_resource.go rename to provider/internal/resources_gen/28_vc_vm_v3_resource.go diff --git a/universal_rebuild/internal/resources_gen/29_vc_vdc_group_add_vdc_action.go b/provider/internal/resources_gen/29_vc_vdc_group_add_vdc_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/29_vc_vdc_group_add_vdc_action.go rename to provider/internal/resources_gen/29_vc_vdc_group_add_vdc_action.go diff --git a/universal_rebuild/internal/resources_gen/29_vc_vdc_group_reconcile_action.go b/provider/internal/resources_gen/29_vc_vdc_group_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/29_vc_vdc_group_reconcile_action.go rename to provider/internal/resources_gen/29_vc_vdc_group_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/29_vc_vdc_group_remove_vdc_action.go b/provider/internal/resources_gen/29_vc_vdc_group_remove_vdc_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/29_vc_vdc_group_remove_vdc_action.go rename to provider/internal/resources_gen/29_vc_vdc_group_remove_vdc_action.go diff --git a/universal_rebuild/internal/resources_gen/29_vc_vdc_group_resource.go b/provider/internal/resources_gen/29_vc_vdc_group_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/29_vc_vdc_group_resource.go rename to provider/internal/resources_gen/29_vc_vdc_group_resource.go diff --git a/universal_rebuild/internal/resources_gen/29_vc_vdc_group_vdc_resource.go b/provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/29_vc_vdc_group_vdc_resource.go rename to provider/internal/resources_gen/29_vc_vdc_group_vdc_resource.go diff --git a/universal_rebuild/internal/resources_gen/2_template_reconcile_action.go b/provider/internal/resources_gen/2_template_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/2_template_reconcile_action.go rename to provider/internal/resources_gen/2_template_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/2_template_resource.go b/provider/internal/resources_gen/2_template_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/2_template_resource.go rename to provider/internal/resources_gen/2_template_resource.go diff --git a/universal_rebuild/internal/resources_gen/32_vmpostgre_resource.go b/provider/internal/resources_gen/32_vmpostgre_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/32_vmpostgre_resource.go rename to provider/internal/resources_gen/32_vmpostgre_resource.go diff --git a/universal_rebuild/internal/resources_gen/50_nextcloud_backup_resource.go b/provider/internal/resources_gen/50_nextcloud_backup_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/50_nextcloud_backup_resource.go rename to provider/internal/resources_gen/50_nextcloud_backup_resource.go diff --git a/universal_rebuild/internal/resources_gen/50_nextcloud_reconcile_action.go b/provider/internal/resources_gen/50_nextcloud_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/50_nextcloud_reconcile_action.go rename to provider/internal/resources_gen/50_nextcloud_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/50_nextcloud_recovery_action.go b/provider/internal/resources_gen/50_nextcloud_recovery_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/50_nextcloud_recovery_action.go rename to provider/internal/resources_gen/50_nextcloud_recovery_action.go diff --git a/universal_rebuild/internal/resources_gen/50_nextcloud_resource.go b/provider/internal/resources_gen/50_nextcloud_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/50_nextcloud_resource.go rename to provider/internal/resources_gen/50_nextcloud_resource.go diff --git a/universal_rebuild/internal/resources_gen/50_nextcloud_restart_action.go b/provider/internal/resources_gen/50_nextcloud_restart_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/50_nextcloud_restart_action.go rename to provider/internal/resources_gen/50_nextcloud_restart_action.go diff --git a/universal_rebuild/internal/resources_gen/81_superset_resource.go b/provider/internal/resources_gen/81_superset_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/81_superset_resource.go rename to provider/internal/resources_gen/81_superset_resource.go diff --git a/universal_rebuild/internal/resources_gen/82_harbor_resource.go b/provider/internal/resources_gen/82_harbor_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/82_harbor_resource.go rename to provider/internal/resources_gen/82_harbor_resource.go diff --git a/universal_rebuild/internal/resources_gen/88_k8s_ziti_controller_resource.go b/provider/internal/resources_gen/88_k8s_ziti_controller_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/88_k8s_ziti_controller_resource.go rename to provider/internal/resources_gen/88_k8s_ziti_controller_resource.go diff --git a/universal_rebuild/internal/resources_gen/89_flask_reconcile_action.go b/provider/internal/resources_gen/89_flask_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/89_flask_reconcile_action.go rename to provider/internal/resources_gen/89_flask_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/89_flask_redeploy_action.go b/provider/internal/resources_gen/89_flask_redeploy_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/89_flask_redeploy_action.go rename to provider/internal/resources_gen/89_flask_redeploy_action.go diff --git a/universal_rebuild/internal/resources_gen/89_flask_resource.go b/provider/internal/resources_gen/89_flask_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/89_flask_resource.go rename to provider/internal/resources_gen/89_flask_resource.go diff --git a/universal_rebuild/internal/resources_gen/90_postgres_backup_resource.go b/provider/internal/resources_gen/90_postgres_backup_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/90_postgres_backup_resource.go rename to provider/internal/resources_gen/90_postgres_backup_resource.go diff --git a/universal_rebuild/internal/resources_gen/90_postgres_database_resource.go b/provider/internal/resources_gen/90_postgres_database_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/90_postgres_database_resource.go rename to provider/internal/resources_gen/90_postgres_database_resource.go diff --git a/universal_rebuild/internal/resources_gen/90_postgres_reconcile_action.go b/provider/internal/resources_gen/90_postgres_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/90_postgres_reconcile_action.go rename to provider/internal/resources_gen/90_postgres_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/90_postgres_recovery_action.go b/provider/internal/resources_gen/90_postgres_recovery_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/90_postgres_recovery_action.go rename to provider/internal/resources_gen/90_postgres_recovery_action.go diff --git a/universal_rebuild/internal/resources_gen/90_postgres_resource.go b/provider/internal/resources_gen/90_postgres_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/90_postgres_resource.go rename to provider/internal/resources_gen/90_postgres_resource.go diff --git a/universal_rebuild/internal/resources_gen/90_postgres_restart_action.go b/provider/internal/resources_gen/90_postgres_restart_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/90_postgres_restart_action.go rename to provider/internal/resources_gen/90_postgres_restart_action.go diff --git a/universal_rebuild/internal/resources_gen/90_postgres_user_resource.go b/provider/internal/resources_gen/90_postgres_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/90_postgres_user_resource.go rename to provider/internal/resources_gen/90_postgres_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/91_redis_reconcile_action.go b/provider/internal/resources_gen/91_redis_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/91_redis_reconcile_action.go rename to provider/internal/resources_gen/91_redis_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/91_redis_resource.go b/provider/internal/resources_gen/91_redis_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/91_redis_resource.go rename to provider/internal/resources_gen/91_redis_resource.go diff --git a/universal_rebuild/internal/resources_gen/92_mongodb_resource.go b/provider/internal/resources_gen/92_mongodb_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/92_mongodb_resource.go rename to provider/internal/resources_gen/92_mongodb_resource.go diff --git a/universal_rebuild/internal/resources_gen/92_mongodb_user_resource.go b/provider/internal/resources_gen/92_mongodb_user_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/92_mongodb_user_resource.go rename to provider/internal/resources_gen/92_mongodb_user_resource.go diff --git a/universal_rebuild/internal/resources_gen/93_rabbitmq_resource.go b/provider/internal/resources_gen/93_rabbitmq_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/93_rabbitmq_resource.go rename to provider/internal/resources_gen/93_rabbitmq_resource.go diff --git a/universal_rebuild/internal/resources_gen/94_lucee_reconcile_action.go b/provider/internal/resources_gen/94_lucee_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/94_lucee_reconcile_action.go rename to provider/internal/resources_gen/94_lucee_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/94_lucee_redeploy_action.go b/provider/internal/resources_gen/94_lucee_redeploy_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/94_lucee_redeploy_action.go rename to provider/internal/resources_gen/94_lucee_redeploy_action.go diff --git a/universal_rebuild/internal/resources_gen/94_lucee_resource.go b/provider/internal/resources_gen/94_lucee_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/94_lucee_resource.go rename to provider/internal/resources_gen/94_lucee_resource.go diff --git a/universal_rebuild/internal/resources_gen/94_lucee_restart_action.go b/provider/internal/resources_gen/94_lucee_restart_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/94_lucee_restart_action.go rename to provider/internal/resources_gen/94_lucee_restart_action.go diff --git a/universal_rebuild/internal/resources_gen/95_nodejs_reconcile_action.go b/provider/internal/resources_gen/95_nodejs_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/95_nodejs_reconcile_action.go rename to provider/internal/resources_gen/95_nodejs_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/95_nodejs_redeploy_action.go b/provider/internal/resources_gen/95_nodejs_redeploy_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/95_nodejs_redeploy_action.go rename to provider/internal/resources_gen/95_nodejs_redeploy_action.go diff --git a/universal_rebuild/internal/resources_gen/95_nodejs_resource.go b/provider/internal/resources_gen/95_nodejs_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/95_nodejs_resource.go rename to provider/internal/resources_gen/95_nodejs_resource.go diff --git a/universal_rebuild/internal/resources_gen/96_pgadmin_redeploy_action.go b/provider/internal/resources_gen/96_pgadmin_redeploy_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/96_pgadmin_redeploy_action.go rename to provider/internal/resources_gen/96_pgadmin_redeploy_action.go diff --git a/universal_rebuild/internal/resources_gen/96_pgadmin_resource.go b/provider/internal/resources_gen/96_pgadmin_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/96_pgadmin_resource.go rename to provider/internal/resources_gen/96_pgadmin_resource.go diff --git a/universal_rebuild/internal/resources_gen/97_nodered_resource.go b/provider/internal/resources_gen/97_nodered_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/97_nodered_resource.go rename to provider/internal/resources_gen/97_nodered_resource.go diff --git a/universal_rebuild/internal/resources_gen/98_http_reconcile_action.go b/provider/internal/resources_gen/98_http_reconcile_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/98_http_reconcile_action.go rename to provider/internal/resources_gen/98_http_reconcile_action.go diff --git a/universal_rebuild/internal/resources_gen/98_http_redeploy_action.go b/provider/internal/resources_gen/98_http_redeploy_action.go similarity index 100% rename from universal_rebuild/internal/resources_gen/98_http_redeploy_action.go rename to provider/internal/resources_gen/98_http_redeploy_action.go diff --git a/universal_rebuild/internal/resources_gen/98_http_resource.go b/provider/internal/resources_gen/98_http_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/98_http_resource.go rename to provider/internal/resources_gen/98_http_resource.go diff --git a/universal_rebuild/internal/resources_gen/99_gitea_resource.go b/provider/internal/resources_gen/99_gitea_resource.go similarity index 100% rename from universal_rebuild/internal/resources_gen/99_gitea_resource.go rename to provider/internal/resources_gen/99_gitea_resource.go diff --git a/universal_rebuild/internal/resources_gen/registry.go b/provider/internal/resources_gen/registry.go similarity index 100% rename from universal_rebuild/internal/resources_gen/registry.go rename to provider/internal/resources_gen/registry.go diff --git a/universal_rebuild/main.go b/provider/main.go similarity index 100% rename from universal_rebuild/main.go rename to provider/main.go diff --git a/universal_rebuild/resources_yaml/100_openwhisk.yaml b/provider/resources_yaml/100_openwhisk.yaml similarity index 100% rename from universal_rebuild/resources_yaml/100_openwhisk.yaml rename to provider/resources_yaml/100_openwhisk.yaml diff --git a/universal_rebuild/resources_yaml/110_dnszone.yaml b/provider/resources_yaml/110_dnszone.yaml similarity index 100% rename from universal_rebuild/resources_yaml/110_dnszone.yaml rename to provider/resources_yaml/110_dnszone.yaml diff --git a/universal_rebuild/resources_yaml/111_dnsrecord.yaml b/provider/resources_yaml/111_dnsrecord.yaml similarity index 100% rename from universal_rebuild/resources_yaml/111_dnsrecord.yaml rename to provider/resources_yaml/111_dnsrecord.yaml diff --git a/universal_rebuild/resources_yaml/112_tenant.yaml b/provider/resources_yaml/112_tenant.yaml similarity index 100% rename from universal_rebuild/resources_yaml/112_tenant.yaml rename to provider/resources_yaml/112_tenant.yaml diff --git a/universal_rebuild/resources_yaml/113_vc_complex.yaml b/provider/resources_yaml/113_vc_complex.yaml similarity index 100% rename from universal_rebuild/resources_yaml/113_vc_complex.yaml rename to provider/resources_yaml/113_vc_complex.yaml diff --git a/universal_rebuild/resources_yaml/114_gitea_complex.yaml b/provider/resources_yaml/114_gitea_complex.yaml similarity index 100% rename from universal_rebuild/resources_yaml/114_gitea_complex.yaml rename to provider/resources_yaml/114_gitea_complex.yaml diff --git a/universal_rebuild/resources_yaml/115_mariadb.yaml b/provider/resources_yaml/115_mariadb.yaml similarity index 100% rename from universal_rebuild/resources_yaml/115_mariadb.yaml rename to provider/resources_yaml/115_mariadb.yaml diff --git a/universal_rebuild/resources_yaml/116_kafka.yaml b/provider/resources_yaml/116_kafka.yaml similarity index 100% rename from universal_rebuild/resources_yaml/116_kafka.yaml rename to provider/resources_yaml/116_kafka.yaml diff --git a/universal_rebuild/resources_yaml/117_nifi.yaml b/provider/resources_yaml/117_nifi.yaml similarity index 100% rename from universal_rebuild/resources_yaml/117_nifi.yaml rename to provider/resources_yaml/117_nifi.yaml diff --git a/universal_rebuild/resources_yaml/119_akhq.yaml b/provider/resources_yaml/119_akhq.yaml similarity index 100% rename from universal_rebuild/resources_yaml/119_akhq.yaml rename to provider/resources_yaml/119_akhq.yaml diff --git a/universal_rebuild/resources_yaml/120_clickhouse.yaml b/provider/resources_yaml/120_clickhouse.yaml similarity index 100% rename from universal_rebuild/resources_yaml/120_clickhouse.yaml rename to provider/resources_yaml/120_clickhouse.yaml diff --git a/universal_rebuild/resources_yaml/12_s3.yaml b/provider/resources_yaml/12_s3.yaml similarity index 100% rename from universal_rebuild/resources_yaml/12_s3.yaml rename to provider/resources_yaml/12_s3.yaml diff --git a/universal_rebuild/resources_yaml/13_s3bucket.yaml b/provider/resources_yaml/13_s3bucket.yaml similarity index 100% rename from universal_rebuild/resources_yaml/13_s3bucket.yaml rename to provider/resources_yaml/13_s3bucket.yaml diff --git a/universal_rebuild/resources_yaml/149_valo_tenant.yaml b/provider/resources_yaml/149_valo_tenant.yaml similarity index 100% rename from universal_rebuild/resources_yaml/149_valo_tenant.yaml rename to provider/resources_yaml/149_valo_tenant.yaml diff --git a/universal_rebuild/resources_yaml/150_k8s_sthutrval_cluster.yaml b/provider/resources_yaml/150_k8s_sthutrval_cluster.yaml similarity index 100% rename from universal_rebuild/resources_yaml/150_k8s_sthutrval_cluster.yaml rename to provider/resources_yaml/150_k8s_sthutrval_cluster.yaml diff --git a/universal_rebuild/resources_yaml/19_vc_org.yaml b/provider/resources_yaml/19_vc_org.yaml similarity index 100% rename from universal_rebuild/resources_yaml/19_vc_org.yaml rename to provider/resources_yaml/19_vc_org.yaml diff --git a/universal_rebuild/resources_yaml/1_dummy.yaml b/provider/resources_yaml/1_dummy.yaml similarity index 100% rename from universal_rebuild/resources_yaml/1_dummy.yaml rename to provider/resources_yaml/1_dummy.yaml diff --git a/universal_rebuild/resources_yaml/20_vc_org_saas.yaml b/provider/resources_yaml/20_vc_org_saas.yaml similarity index 100% rename from universal_rebuild/resources_yaml/20_vc_org_saas.yaml rename to provider/resources_yaml/20_vc_org_saas.yaml diff --git a/universal_rebuild/resources_yaml/21_vc_vdc.yaml b/provider/resources_yaml/21_vc_vdc.yaml similarity index 100% rename from universal_rebuild/resources_yaml/21_vc_vdc.yaml rename to provider/resources_yaml/21_vc_vdc.yaml diff --git a/universal_rebuild/resources_yaml/22_vc_nsxt.yaml b/provider/resources_yaml/22_vc_nsxt.yaml similarity index 100% rename from universal_rebuild/resources_yaml/22_vc_nsxt.yaml rename to provider/resources_yaml/22_vc_nsxt.yaml diff --git a/universal_rebuild/resources_yaml/23_vc_vm.yaml b/provider/resources_yaml/23_vc_vm.yaml similarity index 100% rename from universal_rebuild/resources_yaml/23_vc_vm.yaml rename to provider/resources_yaml/23_vc_vm.yaml diff --git a/universal_rebuild/resources_yaml/25_vcexternalip.yaml b/provider/resources_yaml/25_vcexternalip.yaml similarity index 100% rename from universal_rebuild/resources_yaml/25_vcexternalip.yaml rename to provider/resources_yaml/25_vcexternalip.yaml diff --git a/universal_rebuild/resources_yaml/26_vapp.yaml b/provider/resources_yaml/26_vapp.yaml similarity index 100% rename from universal_rebuild/resources_yaml/26_vapp.yaml rename to provider/resources_yaml/26_vapp.yaml diff --git a/universal_rebuild/resources_yaml/27_vc_vm_v2.yaml b/provider/resources_yaml/27_vc_vm_v2.yaml similarity index 100% rename from universal_rebuild/resources_yaml/27_vc_vm_v2.yaml rename to provider/resources_yaml/27_vc_vm_v2.yaml diff --git a/universal_rebuild/resources_yaml/28_vc_vm_v3.yaml b/provider/resources_yaml/28_vc_vm_v3.yaml similarity index 100% rename from universal_rebuild/resources_yaml/28_vc_vm_v3.yaml rename to provider/resources_yaml/28_vc_vm_v3.yaml diff --git a/universal_rebuild/resources_yaml/29_vc_vdc_group.yaml b/provider/resources_yaml/29_vc_vdc_group.yaml similarity index 100% rename from universal_rebuild/resources_yaml/29_vc_vdc_group.yaml rename to provider/resources_yaml/29_vc_vdc_group.yaml diff --git a/universal_rebuild/resources_yaml/2_template.yaml b/provider/resources_yaml/2_template.yaml similarity index 100% rename from universal_rebuild/resources_yaml/2_template.yaml rename to provider/resources_yaml/2_template.yaml diff --git a/universal_rebuild/resources_yaml/32_vmpostgre.yaml b/provider/resources_yaml/32_vmpostgre.yaml similarity index 100% rename from universal_rebuild/resources_yaml/32_vmpostgre.yaml rename to provider/resources_yaml/32_vmpostgre.yaml diff --git a/universal_rebuild/resources_yaml/50_nextcloud.yaml b/provider/resources_yaml/50_nextcloud.yaml similarity index 100% rename from universal_rebuild/resources_yaml/50_nextcloud.yaml rename to provider/resources_yaml/50_nextcloud.yaml diff --git a/universal_rebuild/resources_yaml/81_superset.yaml b/provider/resources_yaml/81_superset.yaml similarity index 100% rename from universal_rebuild/resources_yaml/81_superset.yaml rename to provider/resources_yaml/81_superset.yaml diff --git a/universal_rebuild/resources_yaml/82_harbor.yaml b/provider/resources_yaml/82_harbor.yaml similarity index 100% rename from universal_rebuild/resources_yaml/82_harbor.yaml rename to provider/resources_yaml/82_harbor.yaml diff --git a/universal_rebuild/resources_yaml/88_k8s_ziti_controller.yaml b/provider/resources_yaml/88_k8s_ziti_controller.yaml similarity index 100% rename from universal_rebuild/resources_yaml/88_k8s_ziti_controller.yaml rename to provider/resources_yaml/88_k8s_ziti_controller.yaml diff --git a/universal_rebuild/resources_yaml/89_flask.yaml b/provider/resources_yaml/89_flask.yaml similarity index 100% rename from universal_rebuild/resources_yaml/89_flask.yaml rename to provider/resources_yaml/89_flask.yaml diff --git a/universal_rebuild/resources_yaml/90_postgres.yaml b/provider/resources_yaml/90_postgres.yaml similarity index 100% rename from universal_rebuild/resources_yaml/90_postgres.yaml rename to provider/resources_yaml/90_postgres.yaml diff --git a/universal_rebuild/resources_yaml/91_redis.yaml b/provider/resources_yaml/91_redis.yaml similarity index 100% rename from universal_rebuild/resources_yaml/91_redis.yaml rename to provider/resources_yaml/91_redis.yaml diff --git a/universal_rebuild/resources_yaml/92_mongodb.yaml b/provider/resources_yaml/92_mongodb.yaml similarity index 100% rename from universal_rebuild/resources_yaml/92_mongodb.yaml rename to provider/resources_yaml/92_mongodb.yaml diff --git a/universal_rebuild/resources_yaml/93_rabbitmq.yaml b/provider/resources_yaml/93_rabbitmq.yaml similarity index 100% rename from universal_rebuild/resources_yaml/93_rabbitmq.yaml rename to provider/resources_yaml/93_rabbitmq.yaml diff --git a/universal_rebuild/resources_yaml/94_lucee.yaml b/provider/resources_yaml/94_lucee.yaml similarity index 100% rename from universal_rebuild/resources_yaml/94_lucee.yaml rename to provider/resources_yaml/94_lucee.yaml diff --git a/universal_rebuild/resources_yaml/95_nodejs.yaml b/provider/resources_yaml/95_nodejs.yaml similarity index 100% rename from universal_rebuild/resources_yaml/95_nodejs.yaml rename to provider/resources_yaml/95_nodejs.yaml diff --git a/universal_rebuild/resources_yaml/96_pgadmin.yaml b/provider/resources_yaml/96_pgadmin.yaml similarity index 100% rename from universal_rebuild/resources_yaml/96_pgadmin.yaml rename to provider/resources_yaml/96_pgadmin.yaml diff --git a/universal_rebuild/resources_yaml/97_nodered.yaml b/provider/resources_yaml/97_nodered.yaml similarity index 100% rename from universal_rebuild/resources_yaml/97_nodered.yaml rename to provider/resources_yaml/97_nodered.yaml diff --git a/universal_rebuild/resources_yaml/98_http.yaml b/provider/resources_yaml/98_http.yaml similarity index 100% rename from universal_rebuild/resources_yaml/98_http.yaml rename to provider/resources_yaml/98_http.yaml diff --git a/universal_rebuild/resources_yaml/99_gitea.yaml b/provider/resources_yaml/99_gitea.yaml similarity index 100% rename from universal_rebuild/resources_yaml/99_gitea.yaml rename to provider/resources_yaml/99_gitea.yaml diff --git a/universal_rebuild/resources_yaml/embed.go b/provider/resources_yaml/embed.go similarity index 100% rename from universal_rebuild/resources_yaml/embed.go rename to provider/resources_yaml/embed.go