Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba50d18f8f | ||
|
|
d9a6d2f29e |
@@ -46,7 +46,7 @@ spec:
|
|||||||
serviceAccountName: fission-console
|
serviceAccountName: fission-console
|
||||||
containers:
|
containers:
|
||||||
- name: console
|
- name: console
|
||||||
image: naeel/fission-console:v0.2.3
|
image: naeel/fission-console:v0.2.5
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8090
|
- containerPort: 8090
|
||||||
env:
|
env:
|
||||||
|
|||||||
+96
-4
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
@@ -10,9 +11,11 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"fission-console/ui"
|
"fission-console/ui"
|
||||||
|
|
||||||
@@ -340,9 +343,8 @@ func (s *server) handleGetFunction(w http.ResponseWriter, r *http.Request, name
|
|||||||
if pkgErr == nil {
|
if pkgErr == nil {
|
||||||
literal, _, _ := unstructured.NestedString(pkg.Object, "spec", "deployment", "literal")
|
literal, _, _ := unstructured.NestedString(pkg.Object, "spec", "deployment", "literal")
|
||||||
if literal != "" {
|
if literal != "" {
|
||||||
decoded, decErr := base64.StdEncoding.DecodeString(literal)
|
if decodedCode, decErr := decodeLiteralToSource(literal); decErr == nil {
|
||||||
if decErr == nil {
|
code = decodedCode
|
||||||
code = string(decoded)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +430,23 @@ func (s *server) handleUpdateFunctionCode(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
writeAnyJSON(w, http.StatusOK, map[string]any{"updated": true, "package": pkgName})
|
updatedPkg, err := s.dyn.Resource(packageGVR).Namespace(s.ns).Get(ctx, pkgName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("get updated package %q: %v", pkgName, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unstructured.SetNestedField(fn.Object, updatedPkg.GetResourceVersion(), "spec", "package", "packageref", "resourceversion"); err != nil {
|
||||||
|
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("set function package resourceversion: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.dyn.Resource(functionGVR).Namespace(s.ns).Update(ctx, fn, metav1.UpdateOptions{}); err != nil {
|
||||||
|
writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("update function %q package ref: %v", name, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeAnyJSON(w, http.StatusOK, map[string]any{"updated": true, "package": pkgName, "package_resourceversion": updatedPkg.GetResourceVersion()})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) handleInvokeFunction(w http.ResponseWriter, r *http.Request, name string) {
|
func (s *server) handleInvokeFunction(w http.ResponseWriter, r *http.Request, name string) {
|
||||||
@@ -702,3 +720,77 @@ func normalizeMethods(in []string) []string {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeLiteralToSource(literal string) (string, error) {
|
||||||
|
decoded, err := base64.StdEncoding.DecodeString(literal)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if utf8.Valid(decoded) {
|
||||||
|
return string(decoded), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(decoded) >= 4 && bytes.Equal(decoded[:4], []byte{'P', 'K', 3, 4}) {
|
||||||
|
if src, zipErr := decodeZipSource(decoded); zipErr == nil {
|
||||||
|
return src, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(decoded), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeZipSource(zipBytes []byte) (string, error) {
|
||||||
|
reader, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
preferred := []string{"main.py", "main.js", "main.go"}
|
||||||
|
for _, name := range preferred {
|
||||||
|
for _, file := range reader.File {
|
||||||
|
if strings.EqualFold(file.Name, name) {
|
||||||
|
content, readErr := readZipFile(file)
|
||||||
|
if readErr != nil {
|
||||||
|
return "", readErr
|
||||||
|
}
|
||||||
|
if utf8.Valid(content) {
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files := make([]*zip.File, 0, len(reader.File))
|
||||||
|
for _, file := range reader.File {
|
||||||
|
if file.FileInfo().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return files[i].Name < files[j].Name
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
content, readErr := readZipFile(file)
|
||||||
|
if readErr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if utf8.Valid(content) {
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("zip archive does not contain utf-8 source files")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readZipFile(file *zip.File) ([]byte, error) {
|
||||||
|
rc, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
return io.ReadAll(rc)
|
||||||
|
}
|
||||||
|
|||||||
@@ -285,3 +285,44 @@
|
|||||||
|
|
||||||
### Ограничения
|
### Ограничения
|
||||||
- Runtime-проблемы Fission (зависания на cold start/таймауты выполнения) в этом изменении не трогались: исправлен только provider-слой валидации и обнаружения изменений.
|
- Runtime-проблемы Fission (зависания на cold start/таймауты выполнения) в этом изменении не трогались: исправлен только provider-слой валидации и обнаружения изменений.
|
||||||
|
|
||||||
|
## 2026-04-15 (дополнение) — Fix отображения кода в Console UI
|
||||||
|
|
||||||
|
### Проблема
|
||||||
|
- В модальном окне редактирования функции (`Edit Code`) для некоторых пакетов отображались байты ZIP (`PK...`) вместо исходного кода.
|
||||||
|
|
||||||
|
### Причина
|
||||||
|
- `GET /console/api/functions/{name}` декодировал `spec.deployment.literal` только из base64, но не обрабатывал архивированный payload.
|
||||||
|
|
||||||
|
### Исправление
|
||||||
|
- В `console/main.go` добавлено декодирование `literal` с поддержкой ZIP:
|
||||||
|
- если payload plain text/utf-8 -> отдаётся как есть
|
||||||
|
- если payload ZIP -> извлекается `main.py`/`main.js`/`main.go` (или первый utf-8 файл)
|
||||||
|
- Обновлён deployment image: `naeel/fission-console:v0.2.4`.
|
||||||
|
|
||||||
|
### Проверка
|
||||||
|
- `GET /console/api/functions/fn-go-acc` теперь возвращает читаемый Go-код, без `PK...` сигнатур.
|
||||||
|
|
||||||
|
## 2026-04-15 (дополнение) — Полный пользовательский прогон UI и invoke-fix
|
||||||
|
|
||||||
|
### Что проверено как пользовательский сценарий
|
||||||
|
- Прогнан массовый invoke через UI API (`POST /console/api/functions/{name}/invoke`) для всех функций в списке.
|
||||||
|
- Итого: `31` функций, из них `25` успешно отработали, `6` вернули 502/timeout (ожидаемо проблемные/сломанные кейсы).
|
||||||
|
- Проверен полный edit flow через UI API на рабочей функции:
|
||||||
|
- `GET function` -> `PUT /code` -> `POST /invoke` -> `PUT /code` (restore) -> `POST /invoke`.
|
||||||
|
|
||||||
|
### Найденный UI-баг и исправление
|
||||||
|
- Баг: после обновления кода через UI следующий invoke мог отдавать старую специализацию/кэш.
|
||||||
|
- Причина: обновлялся только `Package`, но не обновлялся `Function.spec.package.packageref.resourceversion`.
|
||||||
|
- Фикс в `console/main.go`:
|
||||||
|
- после `package update` читается новый `package.resourceVersion`
|
||||||
|
- выполняется update `Function` с новым `packageref.resourceversion`
|
||||||
|
- invoke сразу использует новую (или восстановленную) версию кода.
|
||||||
|
|
||||||
|
### Проверка фикса
|
||||||
|
- После update через UI invoke возвращает новый ответ.
|
||||||
|
- После restore через UI invoke возвращает исходный ответ (без зависания старого кэша).
|
||||||
|
|
||||||
|
### Статус `fn-go-acc`
|
||||||
|
- `fn-go-acc` продолжает падать не из-за UI, а из-за runtime specialization на стороне Fission.
|
||||||
|
- Подтверждено логами `router/executor`: `GetServiceForFunction ... context canceled` и постоянными readiness-fail у poolmgr pod-ов `go-acc`.
|
||||||
|
|||||||
Reference in New Issue
Block a user