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:
+70
-5
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user