fix: FunctionJob зависал в Running — добавлен RequeueAfter polling

This commit is contained in:
“Naeel”
2026-03-08 12:13:15 +04:00
parent e6abc490cd
commit 461ac096f9
8 changed files with 20 additions and 7 deletions
+14 -4
View File
@@ -1,4 +1,4 @@
// Изменено: 2026-03-08
// Изменено: 2026-03-08 (fix: RequeueAfter для poll статуса Job)
// FunctionJobReconciler — контроллер одноразовых запусков функций.
// При создании FunctionJob:
// 1. Ждёт пока Function станет Ready
@@ -14,6 +14,7 @@ package controllers
import (
"context"
"fmt"
"time"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
@@ -180,6 +181,8 @@ func (r *FunctionJobReconciler) Reconcile(ctx context.Context, req ctrl.Request)
}
// syncJobStatus читает статус k8s Job и обновляет FunctionJob.Status.
// Если Job ещё выполняется — запрашивает повторный reconcile через 5 сек
// (Owns не работает кросс-неймспейсно, поэтому используем polling).
func (r *FunctionJobReconciler) syncJobStatus(ctx context.Context, fj *slessv1alpha1.FunctionJob, job *batchv1.Job) (ctrl.Result, error) {
if job.Status.Succeeded > 0 {
now := metav1.Now()
@@ -191,8 +194,13 @@ func (r *FunctionJobReconciler) syncJobStatus(ctx context.Context, fj *slessv1al
fj.Status.Phase = slessv1alpha1.FunctionJobPhaseFailed
fj.Status.CompletionTime = &now
fj.Status.Message = "job failed, check pod logs: kubectl logs -n sless-fn-" + fj.Namespace + " -l functionjob=" + fj.Name
} else {
// Job ещё выполняется — перечитаем через 5 секунд
if err := r.Status().Update(ctx, fj); err != nil {
return ctrl.Result{}, fmt.Errorf("sync job status: %w", err)
}
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
}
// Running — ничего не меняем, перечитаем при следующем reconcile
if err := r.Status().Update(ctx, fj); err != nil {
return ctrl.Result{}, fmt.Errorf("sync job status: %w", err)
}
@@ -251,10 +259,12 @@ func fnEnvVars(fn *slessv1alpha1.Function) []corev1.EnvVar {
func int32Ptr(i int32) *int32 { return &i }
// SetupWithManager регистрирует контроллер и настраивает watch на k8s Job.
// SetupWithManager регистрирует контроллер.
// Owns(&batchv1.Job{}) намеренно убрано: Job создаётся в другом namespace
// (sless-fn-*), где OwnerReference кросс-неймспейсно не работают.
// Вместо этого используется RequeueAfter-polling в syncJobStatus.
func (r *FunctionJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&slessv1alpha1.FunctionJob{}).
Owns(&batchv1.Job{}).
Complete(r)
}
+1 -1
View File
@@ -70,7 +70,7 @@ spec:
containers:
- name: operator
# При обновлении версии оператора — менять тег здесь (не latest!)
image: naeel/sless-operator:v0.1.8
image: naeel/sless-operator:v0.1.10
# Always — чтобы всегда тянуть по точному тегу (не кешировать старый)
imagePullPolicy: Always
ports:
+2 -1
View File
@@ -38,9 +38,10 @@ rules:
resources: ["services", "namespaces"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Secrets — pull-секрет копируется в sless-fn-* namespace при создании функций
# list/watch нужны для controller-runtime cache (информер на secrets)
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
+1
View File
@@ -5,3 +5,4 @@ exports.handle = async (event) => {
const name = event.name || 'World';
return { message: `Hello, ${name}!` };
};
+1
View File
@@ -7,3 +7,4 @@ exports.handle = async (event) => {
const sum = numbers.reduce((acc, n) => acc + n, 0);
return { input: numbers, sum, count: numbers.length };
};
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -33,7 +33,7 @@ func runtimeBaseImage(runtime string) (string, error) {
case "python3.11":
return "naeel/sless-runtime-python3.11:v0.1.0", nil
case "nodejs20":
return "naeel/sless-runtime-nodejs20:v0.1.0", nil
return "naeel/sless-runtime-nodejs20:v0.1.1", nil
default:
return "", fmt.Errorf("unsupported runtime: %q (supported: python3.11, nodejs20)", runtime)
}