v5.0.59: redeploy as git_revision, no separate action resources, gen_v2 follows ARCHITECTURE.md
This commit is contained in:
@@ -2,7 +2,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
nubes = {
|
nubes = {
|
||||||
source = "terra.k8c.ru/nubes-test/nubes"
|
source = "terra.k8c.ru/nubes-test/nubes"
|
||||||
version = "5.0.58"
|
version = "5.0.59"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
nubes = {
|
nubes = {
|
||||||
source = "terra.k8c.ru/nubes-test/nubes"
|
source = "terra.k8c.ru/nubes-test/nubes"
|
||||||
version = "5.0.58"
|
version = "5.0.59"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ terraform {
|
|||||||
required_providers {
|
required_providers {
|
||||||
nubes = {
|
nubes = {
|
||||||
source = "terra.k8c.ru/nubes-test/nubes"
|
source = "terra.k8c.ru/nubes-test/nubes"
|
||||||
version = "5.0.58"
|
version = "5.0.59"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ NUBES_API_ENDPOINT="https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
|
|||||||
TOKEN_FILE="secrets/test.token"
|
TOKEN_FILE="secrets/test.token"
|
||||||
|
|
||||||
# Release versions
|
# Release versions
|
||||||
RELEASE_VERSION="5.0.58"
|
RELEASE_VERSION="5.0.59"
|
||||||
PROVIDER_VERSION="5.0.58"
|
PROVIDER_VERSION="5.0.59"
|
||||||
DOCS_VERSION="5.0.58"
|
DOCS_VERSION="5.0.59"
|
||||||
|
|
||||||
# Docs generation — ONLY from docs_gen/<stand>/ (never from docs/)
|
# Docs generation — ONLY from docs_gen/<stand>/ (never from docs/)
|
||||||
DOCS_GEN_DIR="universal_rebuild/docs_gen/test"
|
DOCS_GEN_DIR="universal_rebuild/docs_gen/test"
|
||||||
|
|||||||
@@ -476,6 +476,57 @@ func (c *UniversalClient) RunInstanceOperationUniversalWithDefaults(ctx context.
|
|||||||
return c.waitForOperationFinish(ctx, opUid, c.operationTimeoutForContext(ctx, state.ServiceId, action))
|
return c.waitForOperationFinish(ctx, opUid, c.operationTimeoutForContext(ctx, state.ServiceId, action))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunRedeployOperation запускает redeploy для сервисов, поддерживающих пересборку из git.
|
||||||
|
// В отличие от modify, redeploy не требует CFS-параметров — операция запускается без них.
|
||||||
|
// Используется генератором (gen_v2) согласно ARCHITECTURE.md.
|
||||||
|
func (c *UniversalClient) RunRedeployOperation(ctx context.Context, instanceUid string, timeoutOverride string) error {
|
||||||
|
state, err := c.GetInstanceState(ctx, instanceUid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if state.OperationIsPending || state.OperationIsInProgress {
|
||||||
|
if err := c.waitForInstanceIdle(ctx, instanceUid, c.idleTimeoutFor(state.ServiceId)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opId := 0
|
||||||
|
for _, op := range state.AvailableOperations {
|
||||||
|
if strings.EqualFold(op.Operation, "redeploy") {
|
||||||
|
opId = op.SvcOperationId
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opId == 0 {
|
||||||
|
return fmt.Errorf("операция redeploy недоступна для экземпляра %s", instanceUid)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"instanceUid": instanceUid,
|
||||||
|
"svcOperationId": opId,
|
||||||
|
"operation": "redeploy",
|
||||||
|
}
|
||||||
|
opUid, err := c.postIgnoreResponse(ctx, "/instanceOperations", payload, true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("не удалось создать операцию redeploy: %w", err)
|
||||||
|
}
|
||||||
|
if opUid == "" {
|
||||||
|
return fmt.Errorf("не удалось получить UID операции redeploy")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, err := c.doRequest(ctx, "POST", fmt.Sprintf("/instanceOperations/%s/run", opUid), map[string]interface{}{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout := c.operationTimeoutForContext(ctx, state.ServiceId, "redeploy")
|
||||||
|
if timeoutOverride != "" {
|
||||||
|
if d, parseErr := time.ParseDuration(timeoutOverride); parseErr == nil {
|
||||||
|
timeout = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.waitForOperationFinish(ctx, opUid, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
// Instance state structures
|
// Instance state structures
|
||||||
|
|
||||||
type ApiOperation struct {
|
type ApiOperation struct {
|
||||||
|
|||||||
@@ -96,6 +96,12 @@ type GenResource struct {
|
|||||||
NeedsStringDefault bool
|
NeedsStringDefault bool
|
||||||
HasDomainParam bool
|
HasDomainParam bool
|
||||||
DomainServiceIDs []int
|
DomainServiceIDs []int
|
||||||
|
// HasRedeploy: сервис поддерживает redeploy (пересборка из git).
|
||||||
|
// Если true — в основной ресурс добавляется поле git_revision.
|
||||||
|
// При изменении git_revision вызывается redeploy вместо modify.
|
||||||
|
// Правило из ARCHITECTURE.md: только redeploy включается как inline action.
|
||||||
|
// restart, recovery, reconcile — исключены (ручные операции, только UI).
|
||||||
|
HasRedeploy bool
|
||||||
// NeedsJsonPlanMod: хотя бы один атрибут имеет data_type: json.
|
// NeedsJsonPlanMod: хотя бы один атрибут имеет data_type: json.
|
||||||
// Управляет добавлением импорта planmodifier в сгенерированный файл.
|
// Управляет добавлением импорта planmodifier в сгенерированный файл.
|
||||||
NeedsJsonPlanMod bool
|
NeedsJsonPlanMod bool
|
||||||
@@ -277,6 +283,37 @@ func loadSpecs(dir string) ([]GenResource, []GenSubresource, []GenAction, error)
|
|||||||
// 3) есть строковые CreateOnlyParams — шаблон генерирует EqualFold в create-only validation (ModifyPlan)
|
// 3) есть строковые CreateOnlyParams — шаблон генерирует EqualFold в create-only validation (ModifyPlan)
|
||||||
// RefSvcId=12 (S3 Object Storage) НЕ генерирует restore-блок — для S3 UUID обязателен точный формат
|
// RefSvcId=12 (S3 Object Storage) НЕ генерирует restore-блок — для S3 UUID обязателен точный формат
|
||||||
gr.NeedsStringsImport = hasRestoreCasingParams(gr.SchemaParams) || analyzeNeedsStrings(gr.ModifyParams) || analyzeNeedsStrings(gr.CreateOnlyRequiredParams) || analyzeNeedsStrings(gr.CreateOnlyParams)
|
gr.NeedsStringsImport = hasRestoreCasingParams(gr.SchemaParams) || analyzeNeedsStrings(gr.ModifyParams) || analyzeNeedsStrings(gr.CreateOnlyRequiredParams) || analyzeNeedsStrings(gr.CreateOnlyParams)
|
||||||
|
|
||||||
|
// --- Action processing (ДО append, чтобы HasRedeploy попал в слайс) ---
|
||||||
|
// Правила из ARCHITECTURE.md:
|
||||||
|
// - redeploy → встроить как git_revision в основной ресурс
|
||||||
|
// - restart, recovery, reconcile → исключить (ручные операции, только UI)
|
||||||
|
// - всё остальное → стандартный action-ресурс (на будущее, если появятся новые)
|
||||||
|
for _, op := range spec.Operations {
|
||||||
|
if op.Kind != "action" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch op.Action {
|
||||||
|
case "redeploy":
|
||||||
|
gr.HasRedeploy = true
|
||||||
|
case "restart", "recovery", "reconcile":
|
||||||
|
// Исключены
|
||||||
|
default:
|
||||||
|
act := GenAction{
|
||||||
|
ServiceName: spec.Name,
|
||||||
|
ServiceID: spec.ServiceID,
|
||||||
|
ActionName: op.Action,
|
||||||
|
OperationName: op.Name,
|
||||||
|
Params: convertParams(op.Params),
|
||||||
|
}
|
||||||
|
act.SchemaParams = act.Params
|
||||||
|
analyzeParams(&act.UsesBool, &act.UsesInt64, &act.UsesString, &act.HasDefaults, &act.NeedsBoolDefault, &act.NeedsInt64Default, &act.NeedsStringDefault, act.SchemaParams)
|
||||||
|
act.NeedsJsonPlanMod = analyzeJsonPlanMod(act.SchemaParams)
|
||||||
|
act.NeedsStringsImport = analyzeNeedsStrings(act.SchemaParams)
|
||||||
|
actions = append(actions, act)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
services = append(services, gr)
|
services = append(services, gr)
|
||||||
|
|
||||||
srByName := map[string]*GenSubresource{}
|
srByName := map[string]*GenSubresource{}
|
||||||
@@ -326,24 +363,6 @@ func loadSpecs(dir string) ([]GenResource, []GenSubresource, []GenAction, error)
|
|||||||
subs = append(subs, *sr)
|
subs = append(subs, *sr)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, op := range spec.Operations {
|
|
||||||
if op.Kind != "action" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
act := GenAction{
|
|
||||||
ServiceName: spec.Name,
|
|
||||||
ServiceID: spec.ServiceID,
|
|
||||||
ActionName: op.Action,
|
|
||||||
OperationName: op.Name,
|
|
||||||
Params: convertParams(op.Params),
|
|
||||||
}
|
|
||||||
act.SchemaParams = act.Params
|
|
||||||
analyzeParams(&act.UsesBool, &act.UsesInt64, &act.UsesString, &act.HasDefaults, &act.NeedsBoolDefault, &act.NeedsInt64Default, &act.NeedsStringDefault, act.SchemaParams)
|
|
||||||
act.NeedsJsonPlanMod = analyzeJsonPlanMod(act.SchemaParams)
|
|
||||||
act.NeedsStringsImport = analyzeNeedsStrings(act.SchemaParams)
|
|
||||||
actions = append(actions, act)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -887,6 +906,12 @@ type {{ToCamel .Name}}Model struct {
|
|||||||
ResourceName types.String ` + "`" + `tfsdk:"resource_name"` + "`" + `
|
ResourceName types.String ` + "`" + `tfsdk:"resource_name"` + "`" + `
|
||||||
OperationTimeout types.String ` + "`" + `tfsdk:"operation_timeout"` + "`" + `
|
OperationTimeout types.String ` + "`" + `tfsdk:"operation_timeout"` + "`" + `
|
||||||
LogLevel types.String ` + "`" + `tfsdk:"log_level"` + "`" + `
|
LogLevel types.String ` + "`" + `tfsdk:"log_level"` + "`" + `
|
||||||
|
{{- if .HasRedeploy }}
|
||||||
|
// --- Redeploy support (ARCHITECTURE.md) ---
|
||||||
|
// git_revision: при изменении вызывает redeploy вместо modify.
|
||||||
|
// Опциональное поле — если не задано, modify работает как обычно.
|
||||||
|
GitRevision types.String ` + "`" + `tfsdk:"git_revision"` + "`" + `
|
||||||
|
{{- end }}
|
||||||
{{- range .SchemaParams }}
|
{{- range .SchemaParams }}
|
||||||
{{ToCamel .Code}} {{ParamType .}} ` + "`" + `tfsdk:"{{ToSnake .Code}}"` + "`" + `
|
{{ToCamel .Code}} {{ParamType .}} ` + "`" + `tfsdk:"{{ToSnake .Code}}"` + "`" + `
|
||||||
{{- end }}
|
{{- end }}
|
||||||
@@ -920,6 +945,12 @@ func (r *{{ToCamel .Name}}Resource) Schema(ctx context.Context, req resource.Sch
|
|||||||
{{- if .IsJson }}PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()},{{- end }}
|
{{- if .IsJson }}PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()},{{- end }}
|
||||||
},
|
},
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .HasRedeploy }}
|
||||||
|
// --- Redeploy support (ARCHITECTURE.md) ---
|
||||||
|
// При изменении git_revision вызывается redeploy вместо modify.
|
||||||
|
// Если не задано — modify работает без изменений.
|
||||||
|
"git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."},
|
||||||
|
{{- end }}
|
||||||
"suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool({{.SuspendOnDestroy}})},
|
"suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool({{.SuspendOnDestroy}})},
|
||||||
"adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool({{.AdoptExistingOnCreate}})},
|
"adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool({{.AdoptExistingOnCreate}})},
|
||||||
{{- range .OutputParams }}
|
{{- range .OutputParams }}
|
||||||
@@ -1291,7 +1322,20 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
|
|||||||
}
|
}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
if !hasServiceParamChanges {
|
{{- if .HasRedeploy }}
|
||||||
|
// --- Redeploy support (ARCHITECTURE.md) ---
|
||||||
|
// Проверяем изменился ли git_revision.
|
||||||
|
// Если да — вызываем redeploy вместо modify.
|
||||||
|
// git_revision = "" (не задано) → обычный modify.
|
||||||
|
redeployRequested := false
|
||||||
|
if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() {
|
||||||
|
if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() {
|
||||||
|
redeployRequested = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
if !hasServiceParamChanges{{if .HasRedeploy}} && !redeployRequested{{end}} {
|
||||||
plan.ID = instanceID
|
plan.ID = instanceID
|
||||||
{{- range .OutputParams }}
|
{{- range .OutputParams }}
|
||||||
plan.{{ToCamel .Code}} = state.{{ToCamel .Code}}
|
plan.{{ToCamel .Code}} = state.{{ToCamel .Code}}
|
||||||
@@ -1300,12 +1344,6 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
params := map[int]string{
|
|
||||||
{{- range .ModifyParams }}
|
|
||||||
{{.ID}}: {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}},
|
|
||||||
{{- end }}
|
|
||||||
}
|
|
||||||
|
|
||||||
operationTimeout := ""
|
operationTimeout := ""
|
||||||
if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() {
|
if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() {
|
||||||
operationTimeout = plan.OperationTimeout.ValueString()
|
operationTimeout = plan.OperationTimeout.ValueString()
|
||||||
@@ -1313,10 +1351,29 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
|
|||||||
if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() {
|
if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() {
|
||||||
ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString())
|
ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString())
|
||||||
}
|
}
|
||||||
if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil {
|
|
||||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
{{- if .HasRedeploy }}
|
||||||
return
|
// --- Redeploy support (ARCHITECTURE.md) ---
|
||||||
|
if redeployRequested {
|
||||||
|
// Вызываем redeploy — пустой params (операция не требует CFS-параметров)
|
||||||
|
if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil {
|
||||||
|
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
{{- end }}
|
||||||
|
params := map[int]string{
|
||||||
|
{{- range .ModifyParams }}
|
||||||
|
{{.ID}}: {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}},
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil {
|
||||||
|
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
{{- if .HasRedeploy }}
|
||||||
}
|
}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
plan.ID = instanceID
|
plan.ID = instanceID
|
||||||
state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), {{.ServiceID}}, plan, []resources_core.StateField{
|
state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), {{.ServiceID}}, plan, []resources_core.StateField{
|
||||||
|
|||||||
Reference in New Issue
Block a user