Fix generator rebuild and release pipeline

This commit is contained in:
Repinoid
2026-09-21 18:08:26 +03:00
parent 1ec6a0fedc
commit 7ecd2aaf44
10 changed files with 131 additions and 139 deletions
@@ -222,12 +222,15 @@ func (r *{{ToCamel .Name}}Resource) ModifyPlan(ctx context.Context, req resource
{{- if .HasRefSvcParams }}
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (eq (ParamType .) "types.String") }}
// refSvc-поле резолвим только для API-запроса.
// config не перезаписываем: пользовательский display name или UUID должен
// пройти в state ровно в том виде, в котором его передал Terraform.
resolved{{ToCamel .Code}} := config.{{ToCamel .Code}}.ValueString()
if !config.{{ToCamel .Code}}.IsNull() && !config.{{ToCamel .Code}}.IsUnknown() {
resolved{{ToCamel .Code}}, err := r.client.ResolveRefSvcParamValue(ctx, {{.RefSvcId}}, config.{{ToCamel .Code}}.ValueString())
var err error
resolved{{ToCamel .Code}}, err = r.client.ResolveRefSvcParamValue(ctx, {{.RefSvcId}}, config.{{ToCamel .Code}}.ValueString())
if err != nil {
resp.Diagnostics.AddWarning("Failed to resolve {{ToSnake .Code}}", err.Error())
} else if resolved{{ToCamel .Code}} != "" && resolved{{ToCamel .Code}} != config.{{ToCamel .Code}}.ValueString() {
config.{{ToCamel .Code}} = types.StringValue(resolved{{ToCamel .Code}})
}
}
{{- end }}
@@ -236,9 +239,13 @@ func (r *{{ToCamel .Name}}Resource) ModifyPlan(ctx context.Context, req resource
params := map[int]string{
{{- range .CreateParams }}
{{- if not (IsNested .) }}
{{- if and (gt .RefSvcId 0) (eq (ParamType .) "types.String") }}
{{.ID}}: resolved{{ToCamel .Code}},
{{- else }}
{{.ID}}: {{ParamFormat . (printf "config.%s" (ToCamel .Code))}},
{{- end }}
{{- end }}
{{- end }}
}
{{- range .CreateParams }}
{{- if (IsNested .) }}
@@ -274,45 +281,22 @@ func (r *{{ToCamel .Name}}Resource) Create(ctx context.Context, req resource.Cre
{{- end }}
{{- if .HasRefSvcParams }}
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
// ══════════════════════════════════════════════════════════════════════════
// ПРАВИЛО TERRAFORM (официальная документация):
// «If an attribute value is configured, it is NEVER valid to change that
// value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что
// написал пользователь). Менять plan запрещено на уровне фреймворка.
//
// ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре.
// Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный).
// После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит:
// «Provider produced inconsistent result after apply».
//
// РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот.
// Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как
// ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API).
// Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный
// регистр в state через strings.EqualFold (сравниваем без учёта регистра).
// ══════════════════════════════════════════════════════════════════════════
original{{ToCamel .Code}} := data.{{ToCamel .Code}}
{{- end }}
{{- end }}
{{- end }}
{{- if .HasRefSvcParams }}
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
{{- if and (gt .RefSvcId 0) (eq (ParamType .) "types.String") }}
// refSvc-поле резолвим только для API-запроса.
// config не перезаписываем: пользовательский display name или UUID должен
// пройти в state ровно в том виде, в котором его передал Terraform.
resolved{{ToCamel .Code}} := data.{{ToCamel .Code}}.ValueString()
if !data.{{ToCamel .Code}}.IsNull() && !data.{{ToCamel .Code}}.IsUnknown() {
resolved{{ToCamel .Code}}, err := r.client.ResolveRefSvcParamValue(ctx, {{.RefSvcId}}, data.{{ToCamel .Code}}.ValueString())
var err error
resolved{{ToCamel .Code}}, err = r.client.ResolveRefSvcParamValue(ctx, {{.RefSvcId}}, data.{{ToCamel .Code}}.ValueString())
if err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
}
if resolved{{ToCamel .Code}} != "" && resolved{{ToCamel .Code}} != data.{{ToCamel .Code}}.ValueString() {
data.{{ToCamel .Code}} = types.StringValue(resolved{{ToCamel .Code}})
}
}
{{- end }}
{{- end }}
{{- end }}
resourceName := data.ResourceName.ValueString()
desiredDomain := ""
{{- if .HasDomainParam }}
@@ -363,7 +347,7 @@ func (r *{{ToCamel .Name}}Resource) Create(ctx context.Context, req resource.Cre
{{- end }}
}, []resources_core.InputField{
{{- range .SchemaParams }}
{{- if or (le .RefSvcId 0) (ne .RefSvcId 12) }}
{{- if eq .RefSvcId 0 }}
{Code: "{{.Code}}", Field: "{{ToCamel .Code}}", Type: "{{.Type}}"},
{{- end }}
{{- end }}
@@ -372,34 +356,6 @@ func (r *{{ToCamel .Name}}Resource) Create(ctx context.Context, req resource.Cre
if resp.Diagnostics.HasError() {
return
}
{{- if .HasRefSvcParams }}
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
if !state.{{ToCamel .Code}}.IsNull() && !state.{{ToCamel .Code}}.IsUnknown() {
resolved{{ToCamel .Code}}, err := r.client.ResolveRefSvcParamValue(ctx, {{.RefSvcId}}, state.{{ToCamel .Code}}.ValueString())
if err != nil {
resp.Diagnostics.AddWarning("Failed to resolve {{ToSnake .Code}}", err.Error())
} else if resolved{{ToCamel .Code}} != "" && resolved{{ToCamel .Code}} != state.{{ToCamel .Code}}.ValueString() {
state.{{ToCamel .Code}} = types.StringValue(resolved{{ToCamel .Code}})
}
}
{{- end }}
{{- end }}
// Restore user-provided casing в state (Create).
// EqualFold = «совпадают ли значения без учёта регистра?»
// Если да — значит API вернул «туже» строку, только в другом регистре.
// Заменяем state на original (то что было в plan/config пользователя).
// Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint.
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
if !original{{ToCamel .Code}}.IsNull() && !original{{ToCamel .Code}}.IsUnknown() && !state.{{ToCamel .Code}}.IsNull() && !state.{{ToCamel .Code}}.IsUnknown() {
if strings.EqualFold(state.{{ToCamel .Code}}.ValueString(), original{{ToCamel .Code}}.ValueString()) {
state.{{ToCamel .Code}} = original{{ToCamel .Code}}
}
}
{{- end }}
{{- end }}
{{- end }}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
@@ -431,7 +387,7 @@ func (r *{{ToCamel .Name}}Resource) Read(ctx context.Context, req resource.ReadR
{{- end }}
}, []resources_core.InputField{
{{- range .SchemaParams }}
{{- if or (le .RefSvcId 0) (ne .RefSvcId 12) }}
{{- if eq .RefSvcId 0 }}
{Code: "{{.Code}}", Field: "{{ToCamel .Code}}", Type: "{{.Type}}"},
{{- end }}
{{- end }}
@@ -440,34 +396,6 @@ func (r *{{ToCamel .Name}}Resource) Read(ctx context.Context, req resource.ReadR
if resp.Diagnostics.HasError() {
return
}
{{- if .HasRefSvcParams }}
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
if !newState.{{ToCamel .Code}}.IsNull() && !newState.{{ToCamel .Code}}.IsUnknown() {
resolved{{ToCamel .Code}}, err := r.client.ResolveRefSvcParamValue(ctx, {{.RefSvcId}}, newState.{{ToCamel .Code}}.ValueString())
if err != nil {
resp.Diagnostics.AddWarning("Failed to resolve {{ToSnake .Code}}", err.Error())
} else if resolved{{ToCamel .Code}} != "" && resolved{{ToCamel .Code}} != newState.{{ToCamel .Code}}.ValueString() {
newState.{{ToCamel .Code}} = types.StringValue(resolved{{ToCamel .Code}})
}
}
{{- end }}
{{- end }}
// Restore user-provided casing в state (Read).
// При чтении у нас нет plan — но предыдущий state уже хранит значение
// в регистре пользователя (после первого Create оно было восстановлено).
// Берём prior state (переменная state) как эталон регистра.
// Если API вернул то же UUID только строчными буквами — восстанавливаем.
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
if !state.{{ToCamel .Code}}.IsNull() && !state.{{ToCamel .Code}}.IsUnknown() && !newState.{{ToCamel .Code}}.IsNull() && !newState.{{ToCamel .Code}}.IsUnknown() {
if strings.EqualFold(newState.{{ToCamel .Code}}.ValueString(), state.{{ToCamel .Code}}.ValueString()) {
newState.{{ToCamel .Code}} = state.{{ToCamel .Code}}
}
}
{{- end }}
{{- end }}
{{- end }}
resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...)
}
@@ -597,7 +525,7 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
{{- end }}
}, []resources_core.InputField{
{{- range .SchemaParams }}
{{- if or (le .RefSvcId 0) (ne .RefSvcId 12) }}
{{- if eq .RefSvcId 0 }}
{Code: "{{.Code}}", Field: "{{ToCamel .Code}}", Type: "{{.Type}}"},
{{- end }}
{{- end }}
@@ -606,33 +534,6 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
if resp.Diagnostics.HasError() {
return
}
{{- if .HasRefSvcParams }}
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
if !state.{{ToCamel .Code}}.IsNull() && !state.{{ToCamel .Code}}.IsUnknown() {
resolved{{ToCamel .Code}}, err := r.client.ResolveRefSvcParamValue(ctx, {{.RefSvcId}}, state.{{ToCamel .Code}}.ValueString())
if err != nil {
resp.Diagnostics.AddWarning("Failed to resolve {{ToSnake .Code}}", err.Error())
} else if resolved{{ToCamel .Code}} != "" && resolved{{ToCamel .Code}} != state.{{ToCamel .Code}}.ValueString() {
state.{{ToCamel .Code}} = types.StringValue(resolved{{ToCamel .Code}})
}
}
{{- end }}
{{- end }}
// Restore user-provided casing в state (Update).
// После API-вызова modify state содержит значения в lower-case от API.
// Plan == config == то что написал пользователь (регистр неизменён).
// EqualFold: если UUID совпадает без учёта регистра — берём из plan.
{{- range .SchemaParams }}
{{- if and (gt .RefSvcId 0) (ne .RefSvcId 12) (eq (ParamType .) "types.String") }}
if !plan.{{ToCamel .Code}}.IsNull() && !plan.{{ToCamel .Code}}.IsUnknown() && !state.{{ToCamel .Code}}.IsNull() && !state.{{ToCamel .Code}}.IsUnknown() {
if strings.EqualFold(state.{{ToCamel .Code}}.ValueString(), plan.{{ToCamel .Code}}.ValueString()) {
state.{{ToCamel .Code}} = plan.{{ToCamel .Code}}
}
}
{{- end }}
{{- end }}
{{- end }}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}