// Изменено: 2026-03-07 // triggers.go — CRUD handlers для Trigger CRD. // Триггеры привязаны к Function через FunctionRef. // Namespace берётся из URL: /v1/namespaces/{namespace}/triggers/{name} package handler import ( "encoding/json" "net/http" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" slessv1alpha1 "gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/api/v1alpha1" ) // triggerRequest — тело запроса для создания триггера. type triggerRequest struct { Name string `json:"name"` Type string `json:"type"` // http | cron FunctionRef string `json:"function"` // имя Function CRD Schedule string `json:"schedule"` // cron-расписание, только для type=cron PreWarmSeconds int32 `json:"pre_warm_seconds"` } // triggerResponse — ответ при чтении триггера. type triggerResponse struct { Name string `json:"name"` Namespace string `json:"namespace"` Type string `json:"type"` FunctionRef string `json:"function"` Schedule string `json:"schedule,omitempty"` Active bool `json:"active"` URL string `json:"url,omitempty"` Message string `json:"message,omitempty"` } // trToResponse конвертирует Trigger CRD в ответ API. func trToResponse(tr *slessv1alpha1.Trigger) triggerResponse { return triggerResponse{ Name: tr.Name, Namespace: tr.Namespace, Type: string(tr.Spec.Type), FunctionRef: tr.Spec.FunctionRef, Schedule: tr.Spec.Schedule, Active: tr.Status.Active, URL: tr.Status.URL, Message: tr.Status.Message, } } // ListTriggers — GET /v1/namespaces/{namespace}/triggers func (h *Handler) ListTriggers(w http.ResponseWriter, r *http.Request) { ns := namespace(r) list := &slessv1alpha1.TriggerList{} if err := h.K8s.List(r.Context(), list, client.InNamespace(ns)); err != nil { writeJSON(w, http.StatusInternalServerError, errResp(err.Error())) return } result := make([]triggerResponse, 0, len(list.Items)) for i := range list.Items { result = append(result, trToResponse(&list.Items[i])) } writeJSON(w, http.StatusOK, result) } // CreateTrigger — POST /v1/namespaces/{namespace}/triggers func (h *Handler) CreateTrigger(w http.ResponseWriter, r *http.Request) { ns := namespace(r) var req triggerRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error())) return } if req.Name == "" || req.Type == "" || req.FunctionRef == "" { writeJSON(w, http.StatusBadRequest, errResp("name, type and function are required")) return } if req.Type == "cron" && req.Schedule == "" { writeJSON(w, http.StatusBadRequest, errResp("schedule is required for cron trigger")) return } tr := &slessv1alpha1.Trigger{ ObjectMeta: metav1.ObjectMeta{ Name: req.Name, Namespace: ns, }, Spec: slessv1alpha1.TriggerSpec{ Type: slessv1alpha1.TriggerType(req.Type), FunctionRef: req.FunctionRef, Schedule: req.Schedule, PreWarmSeconds: req.PreWarmSeconds, }, } if err := h.K8s.Create(r.Context(), tr); err != nil { if errors.IsAlreadyExists(err) { writeJSON(w, http.StatusConflict, errResp("trigger already exists")) return } writeJSON(w, http.StatusInternalServerError, errResp(err.Error())) return } writeJSON(w, http.StatusCreated, trToResponse(tr)) } // GetTrigger — GET /v1/namespaces/{namespace}/triggers/{name} func (h *Handler) GetTrigger(w http.ResponseWriter, r *http.Request) { ns := namespace(r) name := pathVar(r, "name") tr := &slessv1alpha1.Trigger{} if err := h.K8s.Get(r.Context(), client.ObjectKey{Name: name, Namespace: ns}, tr); err != nil { if errors.IsNotFound(err) { writeJSON(w, http.StatusNotFound, errResp("trigger not found")) return } writeJSON(w, http.StatusInternalServerError, errResp(err.Error())) return } writeJSON(w, http.StatusOK, trToResponse(tr)) } // DeleteTrigger — DELETE /v1/namespaces/{namespace}/triggers/{name} func (h *Handler) DeleteTrigger(w http.ResponseWriter, r *http.Request) { ns := namespace(r) name := pathVar(r, "name") tr := &slessv1alpha1.Trigger{} if err := h.K8s.Get(r.Context(), client.ObjectKey{Name: name, Namespace: ns}, tr); err != nil { if errors.IsNotFound(err) { w.WriteHeader(http.StatusNoContent) return } writeJSON(w, http.StatusInternalServerError, errResp(err.Error())) return } if err := h.K8s.Delete(r.Context(), tr); err != nil { writeJSON(w, http.StatusInternalServerError, errResp(err.Error())) return } w.WriteHeader(http.StatusNoContent) }