TOOLS: - yaml-generator: types (API↔YAML), client (proxy/REST + retry) - resource-generator: types (3 resource kinds), loader (pipeline), writers (generated files), params (merge/CreateOnly/ForceNew) - docs-generator, ops-generator — already documented provider: - main.go: entrypoint + build instructions - core: API client (GetInstance, RunOperation, GetInstanceState) - provider: provider registration (New, Resources, Schema) - resources_core: shared CRUD, state refresh, params, validation
271 lines
7.4 KiB
Go
271 lines
7.4 KiB
Go
// Package params — слияние, фильтрация, анализ параметров и ForceNew.
|
|
//
|
|
// Основные операции:
|
|
// - Merge — объединяет параметры из нескольких операций по Code
|
|
// - ComputeCreateOnly — поля, которые есть в create, но отсутствуют в modify
|
|
// - BuildSubresourceForceNewCodes — поля, требующие пересоздания subresource
|
|
// - Analyze — определяет используемые типы (bool/int64/string) для импортов
|
|
// - AnalyzePlanModifiers — ForceNew-поля, требующие planmodifier.*.RequiresReplace()
|
|
package params
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"resource-generator/internal/types"
|
|
)
|
|
|
|
// Merge объединяет несколько наборов параметров, убирая дубликаты по Code.
|
|
func Merge(sets ...[]types.Param) []types.Param {
|
|
seen := map[string]types.Param{}
|
|
for _, s := range sets {
|
|
for _, p := range s {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[key]; !ok {
|
|
seen[key] = p
|
|
}
|
|
}
|
|
}
|
|
out := make([]types.Param, 0, len(seen))
|
|
for _, p := range seen {
|
|
out = append(out, p)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Code < out[j].Code })
|
|
return out
|
|
}
|
|
|
|
// AlignParamTypes выравнивает типы параметров по схеме.
|
|
func AlignParamTypes(params []types.Param, schema []types.Param) []types.Param {
|
|
if len(params) == 0 || len(schema) == 0 {
|
|
return params
|
|
}
|
|
codeType := make(map[string]string, len(schema))
|
|
for _, p := range schema {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if strings.TrimSpace(p.Type) != "" {
|
|
codeType[key] = p.Type
|
|
}
|
|
}
|
|
if len(codeType) == 0 {
|
|
return params
|
|
}
|
|
|
|
for i, p := range params {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if t, ok := codeType[key]; ok && strings.TrimSpace(t) != "" {
|
|
params[i].Type = t
|
|
}
|
|
}
|
|
|
|
return params
|
|
}
|
|
|
|
// ComputeCreateOnly вычисляет набор полей, которые есть только в create (не в modify).
|
|
func ComputeCreateOnly(createParams []types.Param, modifyParams []types.Param) map[string]struct{} {
|
|
modifyCodes := make(map[string]struct{}, len(modifyParams))
|
|
for _, p := range modifyParams {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key == "" {
|
|
continue
|
|
}
|
|
modifyCodes[key] = struct{}{}
|
|
}
|
|
|
|
createOnly := make(map[string]struct{})
|
|
for _, p := range createParams {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if _, ok := modifyCodes[key]; !ok {
|
|
createOnly[key] = struct{}{}
|
|
}
|
|
}
|
|
|
|
return createOnly
|
|
}
|
|
|
|
// MarkCreateOnly проставляет CreateOnly=true для полей, которых нет в modify.
|
|
func MarkCreateOnly(schema []types.Param, createOnly map[string]struct{}) []types.Param {
|
|
if len(schema) == 0 || len(createOnly) == 0 {
|
|
return schema
|
|
}
|
|
|
|
for i, p := range schema {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if _, ok := createOnly[key]; ok {
|
|
schema[i].CreateOnly = true
|
|
}
|
|
}
|
|
|
|
return schema
|
|
}
|
|
|
|
// FilterCreateOnly возвращает только CreateOnly-параметры.
|
|
func FilterCreateOnly(schema []types.Param) []types.Param {
|
|
if len(schema) == 0 {
|
|
return []types.Param{}
|
|
}
|
|
|
|
result := make([]types.Param, 0, len(schema))
|
|
for _, p := range schema {
|
|
if p.CreateOnly {
|
|
result = append(result, p)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// FilterCreateOnlyRequired возвращает обязательные CreateOnly-параметры.
|
|
func FilterCreateOnlyRequired(schema []types.Param) []types.Param {
|
|
if len(schema) == 0 {
|
|
return []types.Param{}
|
|
}
|
|
|
|
result := make([]types.Param, 0, len(schema))
|
|
for _, p := range schema {
|
|
if p.CreateOnly && p.Required {
|
|
result = append(result, p)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Analyze анализирует типы параметров и проставляет флаги uses/needs.
|
|
func Analyze(usesBool *bool, usesInt64 *bool, usesString *bool, hasDefaults *bool, needsBoolDefault *bool, needsInt64Default *bool, needsStringDefault *bool, params []types.Param) {
|
|
for _, p := range params {
|
|
switch strings.ToLower(p.Type) {
|
|
case "bool":
|
|
*usesBool = true
|
|
if p.Default != "" {
|
|
*needsBoolDefault = true
|
|
*hasDefaults = true
|
|
}
|
|
case "int", "int64", "number":
|
|
*usesInt64 = true
|
|
if p.Default != "" {
|
|
*needsInt64Default = true
|
|
*hasDefaults = true
|
|
}
|
|
default:
|
|
*usesString = true
|
|
if p.Default != "" {
|
|
*needsStringDefault = true
|
|
*hasDefaults = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// BuildSubresourceForceNewCodes строит карту параметров, требующих ForceNew.
|
|
func BuildSubresourceForceNewCodes(sr *types.GenSubresource, createOnly map[string]struct{}) map[string]struct{} {
|
|
forceNew := map[string]struct{}{}
|
|
if sr.ModifyOpName == "" {
|
|
for _, p := range sr.SchemaParams {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key != "" {
|
|
forceNew[key] = struct{}{}
|
|
}
|
|
}
|
|
return forceNew
|
|
}
|
|
for key := range createOnly {
|
|
forceNew[key] = struct{}{}
|
|
}
|
|
for _, p := range sr.DeleteParams {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key != "" {
|
|
forceNew[key] = struct{}{}
|
|
}
|
|
}
|
|
return forceNew
|
|
}
|
|
|
|
// MarkForceNew проставляет ForceNew=true для указанных параметров.
|
|
func MarkForceNew(params []types.Param, forceNew map[string]struct{}) []types.Param {
|
|
if len(params) == 0 || len(forceNew) == 0 {
|
|
return params
|
|
}
|
|
for i, p := range params {
|
|
key := strings.ToLower(strings.TrimSpace(p.Code))
|
|
if key == "" {
|
|
continue
|
|
}
|
|
if _, ok := forceNew[key]; ok {
|
|
params[i].ForceNew = true
|
|
}
|
|
}
|
|
return params
|
|
}
|
|
|
|
// FilterForceNew возвращает только ForceNew-параметры.
|
|
func FilterForceNew(params []types.Param) []types.Param {
|
|
result := []types.Param{}
|
|
for _, p := range params {
|
|
if p.ForceNew {
|
|
result = append(result, p)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// AnalyzeJsonPlanMod возвращает true, если хотя бы один параметр — JSON.
|
|
func AnalyzeJsonPlanMod(params []types.Param) bool {
|
|
for _, p := range params {
|
|
if p.IsJson {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HasRestoreCasingParams возвращает true, если есть ref_svc-параметры (кроме S3).
|
|
func HasRestoreCasingParams(params []types.Param) bool {
|
|
for _, p := range params {
|
|
if p.RefSvcId > 0 && p.RefSvcId != 12 && strings.EqualFold(strings.TrimSpace(p.Type), "string") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// AnalyzeNeedsStrings возвращает true, если есть строковые параметры.
|
|
func AnalyzeNeedsStrings(params []types.Param) bool {
|
|
for _, p := range params {
|
|
t := strings.ToLower(strings.TrimSpace(p.Type))
|
|
if t != "bool" && t != "int" && t != "int64" && t != "number" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// AnalyzePlanModifiers анализирует ForceNew-параметры для добавления planmodifier-импортов.
|
|
func AnalyzePlanModifiers(needsBool *bool, needsInt64 *bool, needsString *bool, params []types.Param) {
|
|
for _, p := range params {
|
|
if !p.ForceNew {
|
|
continue
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(p.Type)) {
|
|
case "bool":
|
|
*needsBool = true
|
|
case "int", "int64", "number":
|
|
*needsInt64 = true
|
|
default:
|
|
*needsString = true
|
|
}
|
|
}
|
|
}
|