refactor(gen): шаблон modifier — reconcile(override), Delete стратегия, idempotency-вызов

This commit is contained in:
Repinoid
2026-09-22 21:17:09 +03:00
parent 25e988886d
commit 6b0378cc0d
@@ -4,6 +4,7 @@ const Modifier = `package resources_gen
import (
"context"
"fmt"
"strings"
"terraform-provider-nubes/internal/core"
@@ -36,12 +37,12 @@ type {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource struct {
}
type {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model struct {
ID types.String ` + "`" + `tfsdk:"id"` + "`" + `
{{ToCamel .ServiceName}}ID types.String ` + "`" + `tfsdk:"{{ToSnake .ServiceName}}_id"` + "`" + `
OperationTimeout types.String ` + "`" + `tfsdk:"operation_timeout"` + "`" + `
LogLevel types.String ` + "`" + `tfsdk:"log_level"` + "`" + `
ID types.String {{.bt}}tfsdk:"id"{{.bt}}
{{ToCamel .ServiceName}}ID types.String {{.bt}}tfsdk:"{{ToSnake .ServiceName}}_id"{{.bt}}
OperationTimeout types.String {{.bt}}tfsdk:"operation_timeout"{{.bt}}
LogLevel types.String {{.bt}}tfsdk:"log_level"{{.bt}}
{{- range .SchemaParams }}
{{ToCamel .Code}} {{ParamType .}} ` + "`" + `tfsdk:"{{ToSnake .Code}}"` + "`" + `
{{ToCamel .Code}} {{ParamType .}} {{.bt}}tfsdk:"{{ToSnake .Code}}"{{.bt}}
{{- end }}
}
@@ -76,28 +77,9 @@ func (r *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource) Create
var plan {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() { return }
{{- range .SchemaParams }}
{{- if and .Required (eq (ParamDefaultExpr .) "") }}
if plan.{{ToCamel .Code}}.IsNull() || plan.{{ToCamel .Code}}.IsUnknown() {
resp.Diagnostics.AddError("Missing required attribute", "{{ToSnake .Code}} is required.")
return
}
{{- end }}
{{- end }}
instanceUID := strings.TrimSpace(plan.{{ToCamel .ServiceName}}ID.ValueString())
if instanceUID == "" { resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра"); return }
params := resources_core.CompactParams(map[string]string{
{{- range .SchemaParams }}
"{{.Code}}": {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}},
{{- end }}
})
operationTimeout := ""
if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() }
if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) }
if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "{{.OperationName}}", params, operationTimeout); err != nil {
if err := r.reconcile(ctx, &plan, nil); err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error()); return
}
plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "{{.OperationName}}", "{{.ModifierName}}"))
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}
@@ -125,32 +107,68 @@ func (r *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource) Update
var plan {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() { return }
{{- range .SchemaParams }}
{{- if and .Required (eq (ParamDefaultExpr .) "") }}
if plan.{{ToCamel .Code}}.IsNull() || plan.{{ToCamel .Code}}.IsUnknown() {
resp.Diagnostics.AddError("Missing required attribute", "{{ToSnake .Code}} is required.")
return
}
{{- end }}
{{- end }}
instanceUID := strings.TrimSpace(plan.{{ToCamel .ServiceName}}ID.ValueString())
params := resources_core.CompactParams(map[string]string{
{{- range .SchemaParams }}
"{{.Code}}": {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}},
{{- end }}
})
operationTimeout := ""
if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { operationTimeout = plan.OperationTimeout.ValueString() }
if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) }
if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "{{.OperationName}}", params, operationTimeout); err != nil {
if err := r.reconcile(ctx, &plan, nil); err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error()); return
}
plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "{{.OperationName}}", "{{.ModifierName}}"))
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}
// reconcile — единый путь apply для модификатора (полный payload, досылка в core).
// override — обратные значения (wire-строки), используются только Delete=inverse.
func (r *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource) reconcile(ctx context.Context, model *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model, override map[string]string) error {
instanceUID := strings.TrimSpace(model.{{ToCamel .ServiceName}}ID.ValueString())
if instanceUID == "" { return fmt.Errorf("отсутствует идентификатор экземпляра") }
params := map[string]string{}
{{- range .SchemaParams }}
{{- if .Required }}
if model.{{ToCamel .Code}}.IsNull() || model.{{ToCamel .Code}}.IsUnknown() {
return fmt.Errorf("{{ToSnake .Code}} is required")
}
params["{{.Code}}"] = {{ParamFormat . (printf "model.%s" (ToCamel .Code))}}
{{- else }}
if !model.{{ToCamel .Code}}.IsNull() && !model.{{ToCamel .Code}}.IsUnknown() {
params["{{.Code}}"] = {{ParamFormat . (printf "model.%s" (ToCamel .Code))}}
}
{{- end }}
{{- end }}
for k, v := range override {
params[k] = v
}
operationTimeout := ""
if !model.OperationTimeout.IsNull() && !model.OperationTimeout.IsUnknown() { operationTimeout = model.OperationTimeout.ValueString() }
if !model.LogLevel.IsNull() && !model.LogLevel.IsUnknown() { ctx = core.CtxWithLogLevel(ctx, model.LogLevel.ValueString()) }
{{- if eq .Idempotency "check_before_run" }}
if err := resources_core.RunOperationByCodeIdempotent(ctx, r.client, instanceUID, "{{.OperationName}}", params, operationTimeout); err != nil {
return err
}
{{- else }}
if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "{{.OperationName}}", params, operationTimeout); err != nil {
return err
}
{{- end }}
model.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "{{.OperationName}}", "{{.ModifierName}}"))
return nil
}
func (r *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
// No-op: no confirmed inverse payload exists for this modifier.
var state {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() { return }
{{- if eq .DeleteStrategy "error" }}
resp.Diagnostics.AddError("Запрещено удалять", "Модификатор {{.ServiceName}}.{{.ModifierName}} нельзя удалять: эффект необратим. Обратитесь в техническую поддержку.")
return
{{- else if eq .DeleteStrategy "inverse" }}
override := map[string]string{
{{- range .DeleteParams }}
"{{.Code}}": {{printf "%q" .Value}},
{{- end }}
}
if err := r.reconcile(ctx, &state, override); err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error()); return
}
{{- else }}
resp.Diagnostics.AddWarning("Эффект остаётся на платформе", "Удаление модификатора {{.ServiceName}}.{{.ModifierName}} снимает его из state, но его эффект на платформе остаётся. Откатите вручную при необходимости.")
{{- end }}
}
func (r *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
@@ -159,4 +177,4 @@ func (r *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource) Config
if !ok { resp.Diagnostics.AddError("Error", "Invalid client type"); return }
r.client = client
}
`
`