diff --git a/deployments/k8s/operator.yaml b/deployments/k8s/operator.yaml index 736ed05..57c464c 100644 --- a/deployments/k8s/operator.yaml +++ b/deployments/k8s/operator.yaml @@ -3,7 +3,7 @@ # Состав: # - ConfigMap: не-секретные env vars (S3_ENDPOINT, REGISTRY_HOST и т.д.) # - Secret: секретные данные (S3 keys, postgres DSN, API token, docker auth) -# - Deployment: оператор naeel/sless-operator:v0.1.12 в namespace sless +# - Deployment: оператор naeel/sless-operator:v0.1.23 в namespace sless # - Service: ClusterIP :9090 (REST API) # - Ingress: sless-api.kube5s.ru → :9090 (внешний доступ с TLS) # @@ -70,7 +70,7 @@ spec: containers: - name: operator # При обновлении версии оператора — менять тег здесь (не latest!) - image: naeel/sless-operator:v0.1.12 + image: naeel/sless-operator:v0.1.23 # Always — чтобы всегда тянуть по точному тегу (не кешировать старый) imagePullPolicy: Always ports: diff --git a/internal/api/handler/functions.go b/internal/api/handler/functions.go index d7c4bee..df7a4b3 100644 --- a/internal/api/handler/functions.go +++ b/internal/api/handler/functions.go @@ -115,6 +115,21 @@ func (h *Handler) CreateFunction(w http.ResponseWriter, r *http.Request) { } if err := h.K8s.Create(r.Context(), fn); err != nil { if errors.IsAlreadyExists(err) { + // Если существующая функция в статусе Failed (build провалился, terraform не + // добавил её в state) — удаляем её и пересоздаём, иначе клиент получит 409 навсегда. + existing := &slessv1alpha1.Function{} + if getErr := h.K8s.Get(r.Context(), client.ObjectKey{Name: req.Name, Namespace: ns}, existing); getErr == nil && + existing.Status.Phase == slessv1alpha1.FunctionPhaseFailed { + _ = h.K8s.Delete(r.Context(), existing) + // Создаём заново с теми же параметрами + fn.ResourceVersion = "" + if createErr := h.K8s.Create(r.Context(), fn); createErr != nil { + writeJSON(w, http.StatusInternalServerError, errResp(createErr.Error())) + return + } + writeJSON(w, http.StatusCreated, fnToResponse(fn)) + return + } writeJSON(w, http.StatusConflict, errResp("function already exists")) return } diff --git a/internal/builder/builder.go b/internal/builder/builder.go index b8d1c06..bc52773 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -90,8 +90,9 @@ func (b *Builder) Build(ctx context.Context, namespace, funcName, s3Key string) }, }, Spec: batchv1.JobSpec{ - // Не повторяем при ошибке — контроллер сам перезапустит reconcile - BackoffLimit: int32Ptr(0), + // 2 попытки: при транзиентных сбоях (OOM, network blip) kaniko сможет перезапуститься. + // BackoffLimit=0 приводил к ложным "build job failed" при нагрузке. + BackoffLimit: int32Ptr(2), Completions: int32Ptr(1), Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ diff --git a/run_stress_test.sh b/run_stress_test.sh index fb6470e..dc49cb7 100755 --- a/run_stress_test.sh +++ b/run_stress_test.sh @@ -425,10 +425,10 @@ test_simple() { assert_step "1 changed" grep -q '1 changed' "${log}-r2-mod1.log" assert_step "HTTP 200 after mod1" http_check "$display_url" 200 12 10 - # Mod 2: memory 64 → 96 в time-getter.tf - next_step "[R2-mod2] memory 64→96 (time-getter)" + # Mod 2: memory 96 → 128 в time-getter.tf (начальное значение в time-getter.tf = 96, а не 64) + next_step "[R2-mod2] memory 96→128 (time-getter)" backup_file "$dir/time-getter.tf" - patch_memory "$dir/time-getter.tf" 64 96 + patch_memory "$dir/time-getter.tf" 96 128 assert_step "apply mod2 OK" tf_apply "${log}-r2-mod2.log" "$dir" assert_step "1 changed" grep -q '1 changed' "${log}-r2-mod2.log" assert_step "HTTP 200 after mod2" http_check "$display_url" 200 10 8