feat: configurable timeouts for sless_function and sless_job
- sless_function: build_timeout_sec (optional, default 300s = 5 min) - sless_job: wait_timeout_sec (optional, default 600s = 10 min) - provider v0.1.3 published to terra.k8c.ru
This commit is contained in:
@@ -32,8 +32,9 @@ import (
|
|||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// buildTimeout — максимальное время ожидания kaniko-сборки образа.
|
// defaultBuildTimeoutSec — дефолтный таймаут ожидания kaniko-сборки (300 сек = 5 мин).
|
||||||
const buildTimeout = 5 * time.Minute
|
// Пользователь может переопределить через build_timeout_sec.
|
||||||
|
const defaultBuildTimeoutSec = 300
|
||||||
|
|
||||||
var _ resource.Resource = &FunctionResource{}
|
var _ resource.Resource = &FunctionResource{}
|
||||||
|
|
||||||
@@ -58,8 +59,10 @@ type FunctionModel struct {
|
|||||||
// code_hash — sha256/md5 zip-файла, пользователь задаёт через filemd5().
|
// code_hash — sha256/md5 zip-файла, пользователь задаёт через filemd5().
|
||||||
// Изменение hash → провайдер перезагружает код и запускает пересборку.
|
// Изменение hash → провайдер перезагружает код и запускает пересборку.
|
||||||
CodeHash types.String `tfsdk:"code_hash"`
|
CodeHash types.String `tfsdk:"code_hash"`
|
||||||
Phase types.String `tfsdk:"phase"`
|
// build_timeout_sec — максимальное ожидание kaniko-сборки. Дефолт 300 сек.
|
||||||
ImageRef types.String `tfsdk:"image_ref"`
|
BuildTimeoutSec types.Int64 `tfsdk:"build_timeout_sec"`
|
||||||
|
Phase types.String `tfsdk:"phase"`
|
||||||
|
ImageRef types.String `tfsdk:"image_ref"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *FunctionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
func (r *FunctionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||||
@@ -114,6 +117,13 @@ func (r *FunctionResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
|||||||
"code_hash": schema.StringAttribute{
|
"code_hash": schema.StringAttribute{
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
// build_timeout_sec — таймаут ожидания kaniko-сборки. Дефолт 300 сек (5 мин).
|
||||||
|
// Увеличь если функция с тяжёлыми зависимостями (например torch).
|
||||||
|
"build_timeout_sec": schema.Int64Attribute{
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
MarkdownDescription: "Таймаут ожидания сборки образа в секундах. По умолчанию 300 (5 мин).",
|
||||||
|
},
|
||||||
// phase, image_ref — только для чтения, вычисляются оператором
|
// phase, image_ref — только для чтения, вычисляются оператором
|
||||||
"phase": schema.StringAttribute{
|
"phase": schema.StringAttribute{
|
||||||
Computed: true,
|
Computed: true,
|
||||||
@@ -178,7 +188,11 @@ func (r *FunctionResource) Create(ctx context.Context, req resource.CreateReques
|
|||||||
resp.Diagnostics.AddError("upload code", err.Error())
|
resp.Diagnostics.AddError("upload code", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fn, err = r.client.WaitReady(ctx, ns, fn.Name, buildTimeout)
|
buildSec := plan.BuildTimeoutSec.ValueInt64()
|
||||||
|
if buildSec <= 0 {
|
||||||
|
buildSec = defaultBuildTimeoutSec
|
||||||
|
}
|
||||||
|
fn, err = r.client.WaitReady(ctx, ns, fn.Name, time.Duration(buildSec)*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics.AddError("waiting for function ready", err.Error())
|
resp.Diagnostics.AddError("waiting for function ready", err.Error())
|
||||||
return
|
return
|
||||||
@@ -246,7 +260,11 @@ func (r *FunctionResource) Update(ctx context.Context, req resource.UpdateReques
|
|||||||
resp.Diagnostics.AddError("upload code", err.Error())
|
resp.Diagnostics.AddError("upload code", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fn, err = r.client.WaitReady(ctx, ns, name, buildTimeout)
|
buildSec := plan.BuildTimeoutSec.ValueInt64()
|
||||||
|
if buildSec <= 0 {
|
||||||
|
buildSec = defaultBuildTimeoutSec
|
||||||
|
}
|
||||||
|
fn, err = r.client.WaitReady(ctx, ns, name, time.Duration(buildSec)*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics.AddError("waiting for function ready", err.Error())
|
resp.Diagnostics.AddError("waiting for function ready", err.Error())
|
||||||
return
|
return
|
||||||
@@ -269,20 +287,26 @@ func (r *FunctionResource) Delete(ctx context.Context, req resource.DeleteReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fnToModel конвертирует API-ответ + plan (для локальных полей) → state модель.
|
// fnToModel конвертирует API-ответ + plan (для локальных полей) → state модель.
|
||||||
// plan используется для code_path и code_hash — API их не хранит.
|
// plan используется для code_path, code_hash, build_timeout_sec — API их не хранит.
|
||||||
func fnToModel(plan FunctionModel, fn *client.FunctionResponse) FunctionModel {
|
func fnToModel(plan FunctionModel, fn *client.FunctionResponse) FunctionModel {
|
||||||
|
buildTimeoutSec := plan.BuildTimeoutSec
|
||||||
|
// Если не задан пользователем — записываем дефолт чтобы не было null в state
|
||||||
|
if buildTimeoutSec.IsNull() || buildTimeoutSec.IsUnknown() || buildTimeoutSec.ValueInt64() <= 0 {
|
||||||
|
buildTimeoutSec = types.Int64Value(defaultBuildTimeoutSec)
|
||||||
|
}
|
||||||
return FunctionModel{
|
return FunctionModel{
|
||||||
Namespace: types.StringValue(fn.Namespace),
|
Namespace: types.StringValue(fn.Namespace),
|
||||||
Name: types.StringValue(fn.Name),
|
Name: types.StringValue(fn.Name),
|
||||||
Runtime: types.StringValue(fn.Runtime),
|
Runtime: types.StringValue(fn.Runtime),
|
||||||
Entrypoint: types.StringValue(fn.Entrypoint),
|
Entrypoint: types.StringValue(fn.Entrypoint),
|
||||||
MemoryMB: types.Int64Value(int64(fn.MemoryMB)),
|
MemoryMB: types.Int64Value(int64(fn.MemoryMB)),
|
||||||
TimeoutSec: types.Int64Value(int64(fn.TimeoutSec)),
|
TimeoutSec: types.Int64Value(int64(fn.TimeoutSec)),
|
||||||
EnvVars: plan.EnvVars, // API возвращает null для пустого map — берём из plan
|
EnvVars: plan.EnvVars, // API возвращает null для пустого map — берём из plan
|
||||||
CodePath: plan.CodePath,
|
CodePath: plan.CodePath,
|
||||||
CodeHash: plan.CodeHash,
|
CodeHash: plan.CodeHash,
|
||||||
Phase: types.StringValue(fn.Phase),
|
BuildTimeoutSec: buildTimeoutSec,
|
||||||
ImageRef: types.StringValue(fn.ImageRef),
|
Phase: types.StringValue(fn.Phase),
|
||||||
|
ImageRef: types.StringValue(fn.ImageRef),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ import (
|
|||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// jobTimeout — максимальное время ожидания завершения джоба.
|
// defaultWaitTimeoutSec — дефолтный таймаут ожидания завершения джоба (600 сек = 10 мин).
|
||||||
const jobTimeout = 10 * time.Minute
|
// Пользователь может переопределить через wait_timeout_sec.
|
||||||
|
const defaultWaitTimeoutSec = 600
|
||||||
|
|
||||||
var _ resource.Resource = &JobResource{}
|
var _ resource.Resource = &JobResource{}
|
||||||
|
|
||||||
@@ -43,10 +44,12 @@ func NewJobResource() resource.Resource {
|
|||||||
|
|
||||||
// JobModel — модель состояния terraform для sless_job.
|
// JobModel — модель состояния terraform для sless_job.
|
||||||
type JobModel struct {
|
type JobModel struct {
|
||||||
Namespace types.String `tfsdk:"namespace"`
|
Namespace types.String `tfsdk:"namespace"`
|
||||||
Name types.String `tfsdk:"name"`
|
Name types.String `tfsdk:"name"`
|
||||||
Function types.String `tfsdk:"function"`
|
Function types.String `tfsdk:"function"`
|
||||||
EventJSON types.String `tfsdk:"event_json"`
|
EventJSON types.String `tfsdk:"event_json"`
|
||||||
|
// wait_timeout_sec — максимальное ожидание завершения джоба. Дефолт 600 сек.
|
||||||
|
WaitTimeoutSec types.Int64 `tfsdk:"wait_timeout_sec"`
|
||||||
Phase types.String `tfsdk:"phase"`
|
Phase types.String `tfsdk:"phase"`
|
||||||
StartTime types.String `tfsdk:"start_time"`
|
StartTime types.String `tfsdk:"start_time"`
|
||||||
CompletionTime types.String `tfsdk:"completion_time"`
|
CompletionTime types.String `tfsdk:"completion_time"`
|
||||||
@@ -88,6 +91,12 @@ func (r *JobResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *
|
|||||||
stringplanmodifier.RequiresReplace(),
|
stringplanmodifier.RequiresReplace(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// wait_timeout_sec — сколько ждать завершения джоба. Увеличь если код долго работает (например миграция БД).
|
||||||
|
"wait_timeout_sec": schema.Int64Attribute{
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
MarkdownDescription: "Таймаут ожидания завершения джоба в секундах. По умолчанию 600 (10 мин).",
|
||||||
|
},
|
||||||
// Computed — заполняются после завершения джоба
|
// Computed — заполняются после завершения джоба
|
||||||
"phase": schema.StringAttribute{
|
"phase": schema.StringAttribute{
|
||||||
Computed: true,
|
Computed: true,
|
||||||
@@ -148,7 +157,11 @@ func (r *JobResource) Create(ctx context.Context, req resource.CreateRequest, re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Блокируем apply до завершения джоба (Succeeded или Failed)
|
// Блокируем apply до завершения джоба (Succeeded или Failed)
|
||||||
j, err := r.client.WaitJobDone(ctx, ns, plan.Name.ValueString(), jobTimeout)
|
waitSec := plan.WaitTimeoutSec.ValueInt64()
|
||||||
|
if waitSec <= 0 {
|
||||||
|
waitSec = defaultWaitTimeoutSec
|
||||||
|
}
|
||||||
|
j, err := r.client.WaitJobDone(ctx, ns, plan.Name.ValueString(), time.Duration(waitSec)*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics.AddError("waiting for job to complete", err.Error())
|
resp.Diagnostics.AddError("waiting for job to complete", err.Error())
|
||||||
return
|
return
|
||||||
@@ -198,11 +211,16 @@ func (r *JobResource) Delete(ctx context.Context, req resource.DeleteRequest, re
|
|||||||
|
|
||||||
// jobToModel конвертирует API-ответ → state модель.
|
// jobToModel конвертирует API-ответ → state модель.
|
||||||
func jobToModel(plan JobModel, j *client.JobResponse) JobModel {
|
func jobToModel(plan JobModel, j *client.JobResponse) JobModel {
|
||||||
|
waitTimeoutSec := plan.WaitTimeoutSec
|
||||||
|
if waitTimeoutSec.IsNull() || waitTimeoutSec.IsUnknown() || waitTimeoutSec.ValueInt64() <= 0 {
|
||||||
|
waitTimeoutSec = types.Int64Value(defaultWaitTimeoutSec)
|
||||||
|
}
|
||||||
return JobModel{
|
return JobModel{
|
||||||
Namespace: types.StringValue(j.Namespace),
|
Namespace: types.StringValue(j.Namespace),
|
||||||
Name: types.StringValue(j.Name),
|
Name: types.StringValue(j.Name),
|
||||||
Function: types.StringValue(j.FunctionRef),
|
Function: types.StringValue(j.FunctionRef),
|
||||||
EventJSON: plan.EventJSON, // API отдаёт event_json но берём из plan для консистентности
|
EventJSON: plan.EventJSON,
|
||||||
|
WaitTimeoutSec: waitTimeoutSec,
|
||||||
Phase: types.StringValue(j.Phase),
|
Phase: types.StringValue(j.Phase),
|
||||||
StartTime: types.StringValue(j.StartTime),
|
StartTime: types.StringValue(j.StartTime),
|
||||||
CompletionTime: types.StringValue(j.CompletionTime),
|
CompletionTime: types.StringValue(j.CompletionTime),
|
||||||
|
|||||||
Reference in New Issue
Block a user