feat: big-suite terraform example + provider nodejs zip fix

- examples/big-suite: E-Commerce 10 functions, 6 layers, real depends_on
- provider: nodejs packages now wrapped in ESM zip (buildJSDeployZip)
- provider: loadPackageLiteral reads raw file, nodejs zip via source_dir
- all 10 functions verified working: python/node/ruby/go runtimes
This commit is contained in:
Naeel
2026-04-20 11:38:45 +03:00
parent 4837d42ba1
commit 01df1498d6
22 changed files with 1033 additions and 5 deletions
+70 -5
View File
@@ -85,6 +85,7 @@ type createFunctionRequest struct {
type langEnvDef struct {
Image string
BuilderImage string
Version int // 0 defaults to 3
}
var langEnvMap = map[string]langEnvDef{
@@ -93,7 +94,7 @@ var langEnvMap = map[string]langEnvDef{
"go": {Image: "ghcr.io/fission/go-env", BuilderImage: "ghcr.io/fission/go-builder"},
"php": {Image: "ghcr.io/fission/php-env"},
"ruby": {Image: "ghcr.io/fission/ruby-env"},
"perl": {Image: "ghcr.io/fission/perl-env"},
"perl": {Image: "ghcr.io/fission/perl-env", Version: 1},
}
type updateCodeRequest struct {
@@ -320,7 +321,12 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) {
defer envCancel()
envName, err := s.ensureEnvironment(envCtx, ns, req.Language)
if err != nil {
writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("ensure environment: %v", err))
// unsupported language — это клиентская ошибка → 400
if strings.Contains(err.Error(), "unsupported language") {
writeJSONError(w, http.StatusBadRequest, err.Error())
} else {
writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("ensure environment: %v", err))
}
return
}
req.Environment = envName
@@ -512,8 +518,12 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) {
}
func (s *server) buildLangEnvironment(name string, ns string, def langEnvDef) *unstructured.Unstructured {
envVersion := int64(3)
if def.Version != 0 {
envVersion = int64(def.Version)
}
spec := map[string]any{
"version": int64(3),
"version": envVersion,
"runtime": map[string]any{
"image": def.Image,
},
@@ -746,7 +756,16 @@ func buildJSDeployZip(code string) ([]byte, error) {
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
// main.js — единственный файл: ESM wrapper + код пользователя инлайн через new Function
// package.json: объявляем ESM тип чтобы Node.js трактовал .js как ESM модуль
pkgfw, err := zw.Create("package.json")
if err != nil {
return nil, err
}
if _, err := pkgfw.Write([]byte(`{"type":"module"}`)); err != nil {
return nil, err
}
// main.js — ESM wrapper + код пользователя инлайн через new Function
// new Function безопасно изолирует module/exports от глобального контекста
codeJSON, err := json.Marshal(code)
if err != nil {
@@ -780,10 +799,34 @@ export default async function(ctx) {
return buf.Bytes(), nil
}
// buildScriptZip wraps code into a zip file with the given filename.
// Used for PHP, Ruby, Perl where the environment requires a named source file.
func buildScriptZip(code, filename string) ([]byte, error) {
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
fw, err := zw.Create(filename)
if err != nil {
return nil, err
}
if _, err := fw.Write([]byte(code)); err != nil {
return nil, err
}
if err := zw.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func defaultEntrypoint(lang string) string {
switch lang {
case "nodejs", "php", "ruby", "perl":
case "nodejs":
return "main"
case "php":
return "main.php::handler"
case "ruby":
return "handler"
case "perl":
return "handler"
case "go":
return "Handler"
default:
@@ -1255,6 +1298,28 @@ func (s *server) ensureUserNamespace(ctx context.Context, ns string) error {
return fmt.Errorf("create namespace %s: %w", ns, err)
}
// 1a. ServiceAccounts для Fission в user namespace.
//
// Fission pool pods (fetcher sidecar) запускаются в user namespace и требуют
// `serviceAccountName: fission-fetcher` в том же namespace. Executor (с SERVICEACCOUNT_CHECK_ENABLED=false)
// не создаёт эти SA автоматически → pool pods падают с "serviceaccount not found".
// Создаём явно при каждом вызове (idempotent через IsAlreadyExists).
saGVR := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}
for _, saName := range []string{"fission-fetcher", "fission-builder"} {
saObj := &unstructured.Unstructured{Object: map[string]any{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]any{
"name": saName,
"namespace": ns,
},
}}
_, saErr := s.dyn.Resource(saGVR).Namespace(ns).Create(ctx, saObj, metav1.CreateOptions{})
if saErr != nil && !apierrors.IsAlreadyExists(saErr) {
log.Printf("ensureUserNamespace: create SA %s/%s: %v", ns, saName, saErr)
}
}
// 1b. RoleBindings для Fission SA в user namespace.
//
// Проблема: Fission компоненты (executor, router, buildermgr и др.) работают в namespace