refactor(gen): шаблон modifier — reconcile(override), Delete стратегия, idempotency-вызов
This commit is contained in:
@@ -4,6 +4,7 @@ const Modifier = `package resources_gen
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"terraform-provider-nubes/internal/core"
|
"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 {
|
type {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model struct {
|
||||||
ID types.String ` + "`" + `tfsdk:"id"` + "`" + `
|
ID types.String {{.bt}}tfsdk:"id"{{.bt}}
|
||||||
{{ToCamel .ServiceName}}ID types.String ` + "`" + `tfsdk:"{{ToSnake .ServiceName}}_id"` + "`" + `
|
{{ToCamel .ServiceName}}ID types.String {{.bt}}tfsdk:"{{ToSnake .ServiceName}}_id"{{.bt}}
|
||||||
OperationTimeout types.String ` + "`" + `tfsdk:"operation_timeout"` + "`" + `
|
OperationTimeout types.String {{.bt}}tfsdk:"operation_timeout"{{.bt}}
|
||||||
LogLevel types.String ` + "`" + `tfsdk:"log_level"` + "`" + `
|
LogLevel types.String {{.bt}}tfsdk:"log_level"{{.bt}}
|
||||||
{{- range .SchemaParams }}
|
{{- range .SchemaParams }}
|
||||||
{{ToCamel .Code}} {{ParamType .}} ` + "`" + `tfsdk:"{{ToSnake .Code}}"` + "`" + `
|
{{ToCamel .Code}} {{ParamType .}} {{.bt}}tfsdk:"{{ToSnake .Code}}"{{.bt}}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,28 +77,9 @@ func (r *{{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Resource) Create
|
|||||||
var plan {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model
|
var plan {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model
|
||||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||||
if resp.Diagnostics.HasError() { return }
|
if resp.Diagnostics.HasError() { return }
|
||||||
{{- range .SchemaParams }}
|
if err := r.reconcile(ctx, &plan, nil); err != nil {
|
||||||
{{- 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 {
|
|
||||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error()); return
|
resp.Diagnostics.AddError("Ошибка клиента", err.Error()); return
|
||||||
}
|
}
|
||||||
plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "{{.OperationName}}", "{{.ModifierName}}"))
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
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
|
var plan {{ToCamel (printf "%s_%s" .ServiceName .ModifierName)}}Model
|
||||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||||
if resp.Diagnostics.HasError() { return }
|
if resp.Diagnostics.HasError() { return }
|
||||||
{{- range .SchemaParams }}
|
if err := r.reconcile(ctx, &plan, nil); err != nil {
|
||||||
{{- 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 {
|
|
||||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error()); return
|
resp.Diagnostics.AddError("Ошибка клиента", err.Error()); return
|
||||||
}
|
}
|
||||||
plan.ID = types.StringValue(resources_core.BuildActionID(instanceUID, "{{.OperationName}}", "{{.ModifierName}}"))
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
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) {
|
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) {
|
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 }
|
if !ok { resp.Diagnostics.AddError("Error", "Invalid client type"); return }
|
||||||
r.client = client
|
r.client = client
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
Reference in New Issue
Block a user