diff --git a/console/deploy/console.yaml b/console/deploy/console.yaml index 491177f..f8a3a0f 100644 --- a/console/deploy/console.yaml +++ b/console/deploy/console.yaml @@ -58,7 +58,7 @@ spec: serviceAccountName: fission-console containers: - name: console - image: naeel/fission-console:v1.3.88 + image: naeel/fission-console:v1.3.89 imagePullPolicy: Always ports: - containerPort: 8090 diff --git a/console/internal/api/function_code.go b/console/internal/api/function_code.go index 91fa34a..ba700ad 100644 --- a/console/internal/api/function_code.go +++ b/console/internal/api/function_code.go @@ -35,17 +35,26 @@ import ( // buildDeployArchive упаковывает исходный код в байты для deployment Package. // Для nodejs — ESM-обёртка (package.json + main.js). // Для php/ruby — zip с одним файлом скрипта. -// Для остальных (python) — raw bytes кода. -func buildDeployArchive(lang, code string) ([]byte, error) { +// Для остальных (python) — raw bytes кода (или zip если есть deps). +// deps — содержимое файла зависимостей (requirements.txt, Gemfile, composer.json). +// Если deps пустой — поведение как раньше. +func buildDeployArchive(lang, code, deps string) ([]byte, error) { switch lang { case "nodejs": + // TODO: поддержка package.json с deps для nodejs — пока игнорируем deps return runtime.BuildJSDeployZip(code) case "php": + if deps != "" { + return runtime.BuildScriptZipWithDeps(code, "main.php", deps, "composer.json") + } return runtime.BuildScriptZip(code, "main.php") case "ruby": + if deps != "" { + return runtime.BuildScriptZipWithDeps(code, "handler.rb", deps, "Gemfile") + } return runtime.BuildScriptZip(code, "handler.rb") - default: - return []byte(code), nil + default: // python + return runtime.BuildPythonZip(code, deps) } } @@ -173,7 +182,7 @@ func (s *Server) handleCreateFunction(w http.ResponseWriter, r *http.Request) { "buildcommand": "build", } } else { - deployBytes, archiveErr := buildDeployArchive(req.Language, req.Code) + deployBytes, archiveErr := buildDeployArchive(req.Language, req.Code, req.Deps) if archiveErr != nil { writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("build %s archive: %v", req.Language, archiveErr)) return @@ -329,7 +338,7 @@ func (s *Server) handleUpdateFunctionCode(w http.ResponseWriter, r *http.Request // Определяем язык из аннотации — нужен для правильной упаковки lang, _, _ := unstructured.NestedString(fn.Object, "metadata", "annotations", "fission-console/language") - deployBytes, archiveErr := buildDeployArchive(lang, req.Code) + deployBytes, archiveErr := buildDeployArchive(lang, req.Code, req.Deps) if archiveErr != nil { writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("build %s archive: %v", lang, archiveErr)) return diff --git a/console/internal/model/types.go b/console/internal/model/types.go index 3f3ffd9..74e115f 100644 --- a/console/internal/model/types.go +++ b/console/internal/model/types.go @@ -5,11 +5,14 @@ package model // CreateFunctionRequest — тело POST /console/api/functions. // TTL пустой → функция живёт вечно; "1d", "24h" — протухнет через указанное время. +// Deps — содержимое файла зависимостей: requirements.txt (python), package.json deps (nodejs), +// Gemfile (ruby), composer.json (php). Если задан — код упаковывается в zip вместе с deps-файлом. type CreateFunctionRequest struct { Name string `json:"name"` Language string `json:"language"` Environment string `json:"environment"` Code string `json:"code"` + Deps string `json:"deps"` // содержимое файла зависимостей (опционально) Entrypoint string `json:"entrypoint"` Route string `json:"route"` Methods []string `json:"methods"` @@ -51,6 +54,7 @@ type CreateKWTriggerRequest struct { // UpdateCodeRequest — тело PUT /console/api/functions/:name/code. type UpdateCodeRequest struct { Code string `json:"code"` + Deps string `json:"deps"` // содержимое файла зависимостей (опционально) Timeout int64 `json:"timeout"` } diff --git a/console/internal/runtime/helpers.go b/console/internal/runtime/helpers.go index 91b2889..9ae9205 100644 --- a/console/internal/runtime/helpers.go +++ b/console/internal/runtime/helpers.go @@ -25,3 +25,28 @@ func buildZip(fileName string, content []byte) ([]byte, error) { } return buf.Bytes(), nil } + +// buildZipTwo создаёт zip-архив с двумя файлами. +// Используется когда пользователь указал файл зависимостей (requirements.txt и т.д.). +func buildZipTwo(file1, file2 string, content1, content2 []byte) ([]byte, error) { + var buf bytes.Buffer + zw := zip.NewWriter(&buf) + + for _, f := range []struct { + name string + content []byte + }{{file1, content1}, {file2, content2}} { + fw, err := zw.Create(f.name) + if err != nil { + return nil, err + } + if _, err := fw.Write(f.content); err != nil { + return nil, err + } + } + + if err := zw.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil +} diff --git a/console/internal/runtime/script.go b/console/internal/runtime/script.go index da631b7..f379359 100644 --- a/console/internal/runtime/script.go +++ b/console/internal/runtime/script.go @@ -11,3 +11,20 @@ package runtime func BuildScriptZip(code, fileName string) ([]byte, error) { return buildZip(fileName, []byte(code)) } + +// BuildScriptZipWithDeps создаёт zip с кодом и файлом зависимостей. +// fileName — имя файла кода (handler.rb, main.php и т.д.) +// depsName — имя файла зависимостей (Gemfile, composer.json и т.д.) +func BuildScriptZipWithDeps(code, fileName, deps, depsName string) ([]byte, error) { + return buildZipTwo(fileName, depsName, []byte(code), []byte(deps)) +} + +// BuildPythonZip создаёт zip с main.py (и опционально requirements.txt). +// Если deps пустой — возвращает raw bytes кода (текущее поведение Python). +// Если deps задан — zip с main.py + requirements.txt для pip install. +func BuildPythonZip(code, deps string) ([]byte, error) { + if deps == "" { + return []byte(code), nil + } + return buildZipTwo("main.py", "requirements.txt", []byte(code), []byte(deps)) +} diff --git a/console/ui/index.html b/console/ui/index.html index 905eed1..421d8b2 100644 --- a/console/ui/index.html +++ b/console/ui/index.html @@ -103,7 +103,7 @@