diff --git a/console/deploy/console.yaml b/console/deploy/console.yaml index 1aa0bae..004d761 100644 --- a/console/deploy/console.yaml +++ b/console/deploy/console.yaml @@ -46,7 +46,7 @@ spec: serviceAccountName: fission-console containers: - name: console - image: naeel/fission-console:v0.8.13 + image: naeel/fission-console:v0.8.14 ports: - containerPort: 8090 env: diff --git a/console/main.go b/console/main.go index 9301469..9752723 100644 --- a/console/main.go +++ b/console/main.go @@ -254,6 +254,7 @@ func main() { mux.HandleFunc("/console/api/functions/", auth(s.handleFunctionsAction)) mux.HandleFunc("/console/api/httptriggers", auth(s.handleList(httpTrigGVR))) mux.HandleFunc("/console/api/timetriggers", auth(s.handleList(timeTrigGVR))) + mux.HandleFunc("/console/api/ns/status", auth(s.handleNSStatus)) mux.HandleFunc("/console/api/ai/check", auth(s.handleAICheck)) // --- ai/ask feature (удалить строку чтобы выкосить роут) --- mux.HandleFunc("/console/api/ai/ask", auth(s.handleAIAsk)) @@ -1721,6 +1722,99 @@ func (s *server) ensureUserNamespace(ctx context.Context, ns string) error { return nil } +// handleNSStatus возвращает статус инициализации пользовательского namespace. +// Используется UI для отображения прогресса при первом входе. +func (s *server) handleNSStatus(w http.ResponseWriter, r *http.Request) { + ns := s.userNS(r) + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + fissionNS := os.Getenv("FISSION_SYSTEM_NAMESPACE") + if fissionNS == "" { + fissionNS = "fission" + } + + type stageInfo struct { + Name string `json:"name"` + Done bool `json:"done"` + } + stages := []stageInfo{ + {Name: "Создание пространства имён"}, + {Name: "Регистрация в Fission"}, + {Name: "Прогрев окружений"}, + } + + // Stage 1: NS существует и Active (ensureUserNS уже вызван auth middleware) + nsObj, err := s.dyn.Resource(namespaceGVR).Get(ctx, ns, metav1.GetOptions{}) + if err == nil { + phase, _, _ := unstructured.NestedString(nsObj.Object, "status", "phase") + stages[0].Done = phase == "Active" + } + + // Stage 2: NS в FISSION_RESOURCE_NAMESPACES executor deployment + if stages[0].Done { + execDep, err2 := s.dyn.Resource(deploymentGVR).Namespace(fissionNS).Get(ctx, "executor", metav1.GetOptions{}) + if err2 == nil { + containers, _, _ := unstructured.NestedSlice(execDep.Object, "spec", "template", "spec", "containers") + for _, c := range containers { + cont, ok := c.(map[string]any) + if !ok { + continue + } + envs, _, _ := unstructured.NestedSlice(cont, "env") + for _, e := range envs { + env, ok := e.(map[string]any) + if !ok { + continue + } + if env["name"] == "FISSION_RESOURCE_NAMESPACES" { + if v, ok := env["value"].(string); ok { + for _, p := range strings.Split(v, ",") { + if strings.TrimSpace(p) == ns { + stages[1].Done = true + } + } + } + } + } + break + } + } + } + + // Stage 3: executor pod Running + Ready + if stages[1].Done { + podGVR := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} + podList, err3 := s.dyn.Resource(podGVR).Namespace(fissionNS).List(ctx, metav1.ListOptions{ + LabelSelector: "svc=executor", + }) + if err3 == nil { + for _, pod := range podList.Items { + phase, _, _ := unstructured.NestedString(pod.Object, "status", "phase") + if phase != "Running" { + continue + } + conditions, _, _ := unstructured.NestedSlice(pod.Object, "status", "conditions") + for _, c := range conditions { + cond, ok := c.(map[string]any) + if !ok { + continue + } + if cond["type"] == "Ready" && cond["status"] == "True" { + stages[2].Done = true + } + } + } + } + } + + ready := stages[0].Done && stages[1].Done && stages[2].Done + writeAnyJSON(w, http.StatusOK, map[string]any{ + "ready": ready, + "stages": stages, + }) +} + func (s *server) validateDeckToken(token, env string) error { cacheKey := env + ":" + token if v, ok := s.tokenCache.Load(cacheKey); ok { diff --git a/console/ui/index.html b/console/ui/index.html index 0ba797d..20fcbc3 100644 --- a/console/ui/index.html +++ b/console/ui/index.html @@ -307,13 +307,26 @@
+
Токен: Личный кабинет → Профиль пользователя → Токены
+ + +