From 74815facce65272063b11534ed5d97655895d69d Mon Sep 17 00:00:00 2001 From: Naeel Date: Sun, 19 Apr 2026 17:10:01 +0300 Subject: [PATCH] fix: route isolation, 404 on invoke/delete nonexistent (v0.6.8) - Bug#1: namespace-prefix routes to avoid cross-user collision Route format: /{ns-last-12-chars}/{fn-name} - Bug#2: INVOKE nonexistent function -> HTTP 404 (was 200) - Bug#3: DELETE nonexistent function -> HTTP 404 (was 200) --- console/main.go | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/console/main.go b/console/main.go index 14a9b18..823a832 100644 --- a/console/main.go +++ b/console/main.go @@ -314,7 +314,12 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) { req.Entrypoint = defaultEntrypoint(req.Language) } if req.Route == "" { - req.Route = "/" + req.Name + // namespace-prefix route to avoid collisions between users + nsShort := s.userNS(r) + if len(nsShort) > 12 { + nsShort = nsShort[len(nsShort)-12:] + } + req.Route = "/" + nsShort + "/" + req.Name } if !strings.HasPrefix(req.Route, "/") { req.Route = "/" + req.Route @@ -885,6 +890,16 @@ func (s *server) handleInvokeFunction(w http.ResponseWriter, r *http.Request, na ctx, cancel := context.WithTimeout(r.Context(), invokeTimeout) defer cancel() + // check function exists in k8s before invoke + if _, err2 := s.dyn.Resource(functionGVR).Namespace(s.userNS(r)).Get(ctx, name, metav1.GetOptions{}); err2 != nil { + if apierrors.IsNotFound(err2) { + writeJSONError(w, http.StatusNotFound, fmt.Sprintf("function %q not found", name)) + return + } + writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("get function %q: %v", name, err2)) + return + } + invokeURL := fmt.Sprintf("%s/fission-function/v2/functions/%s", s.routerURL, name) invokeMethod := http.MethodPost triggers, err := s.dyn.Resource(httpTrigGVR).Namespace(s.userNS(r)).List(ctx, metav1.ListOptions{}) @@ -1322,10 +1337,16 @@ func (s *server) handleDeleteFunction(w http.ResponseWriter, r *http.Request, na var pkgName, envName string fn, err := s.dyn.Resource(functionGVR).Namespace(s.userNS(r)).Get(ctx, name, metav1.GetOptions{}) - if err == nil { - pkgName, _, _ = unstructured.NestedString(fn.Object, "spec", "package", "packageref", "name") - envName, _, _ = unstructured.NestedString(fn.Object, "spec", "environment", "name") + if err != nil { + if apierrors.IsNotFound(err) { + writeJSONError(w, http.StatusNotFound, fmt.Sprintf("function %q not found", name)) + return + } + writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("get function %q: %v", name, err)) + return } + pkgName, _, _ = unstructured.NestedString(fn.Object, "spec", "package", "packageref", "name") + envName, _, _ = unstructured.NestedString(fn.Object, "spec", "environment", "name") triggers, err := s.dyn.Resource(httpTrigGVR).Namespace(s.userNS(r)).List(ctx, metav1.ListOptions{}) if err == nil {