From c40fc4702510a8a1c964201a4415b149e96315a7 Mon Sep 17 00:00:00 2001 From: Naeel Date: Wed, 15 Apr 2026 19:47:41 +0300 Subject: [PATCH] =?UTF-8?q?console=20v0.4.5:=20=D1=80=D1=83=D1=81=D0=B8?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D1=8F=20UI,=20Go/TF=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=B4=D1=83=D0=BF=D1=80=D0=B5=D0=B6=D0=B4?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F,=20handler.go=20=D0=B2=20preferred?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + console/main.go | 139 ++++++++++++++++++++++++--- console/ui/index.html | 150 ++++++++++++++++++++---------- examples/go-hello/code/go.mod | 3 + examples/go-hello/code/handler.go | 14 +++ examples/go-hello/code/main.go | 26 ------ examples/go-hello/main.tf | 24 +++-- 7 files changed, 259 insertions(+), 98 deletions(-) create mode 100644 examples/go-hello/code/go.mod create mode 100644 examples/go-hello/code/handler.go delete mode 100644 examples/go-hello/code/main.go diff --git a/.gitignore b/.gitignore index 128e7cf..7fc6f93 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ examples/*/dist/ # Provider binaries terraform-provider-fission terraform-provider-fission_* +console/fission-console diff --git a/console/main.go b/console/main.go index 9196f82..47b0efa 100644 --- a/console/main.go +++ b/console/main.go @@ -58,6 +58,7 @@ type server struct { type createFunctionRequest struct { Name string `json:"name"` + Language string `json:"language"` Environment string `json:"environment"` Code string `json:"code"` Entrypoint string `json:"entrypoint"` @@ -65,6 +66,20 @@ type createFunctionRequest struct { Methods []string `json:"methods"` } +type langEnvDef struct { + Image string + BuilderImage string +} + +var langEnvMap = map[string]langEnvDef{ + "python": {Image: "ghcr.io/fission/python-env"}, + "nodejs": {Image: "ghcr.io/fission/node-env"}, + "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"}, +} + type updateCodeRequest struct { Code string `json:"code"` } @@ -202,13 +217,38 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) { } req.Name = strings.TrimSpace(req.Name) + req.Language = strings.TrimSpace(req.Language) req.Environment = strings.TrimSpace(req.Environment) req.Code = strings.TrimSpace(req.Code) req.Entrypoint = strings.TrimSpace(req.Entrypoint) req.Route = strings.TrimSpace(req.Route) + // Resolve language → environment (auto-create if needed) + if req.Language != "" { + langDef, ok := langEnvMap[req.Language] + if !ok { + writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("unsupported language: %q", req.Language)) + return + } + envName := "console-" + req.Language + "-env" + ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) + defer cancel() + _, err := s.dyn.Resource(environmentGVR).Namespace(s.ns).Get(ctx, envName, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + env := s.buildLangEnvironment(envName, langDef) + if _, err := s.dyn.Resource(environmentGVR).Namespace(s.ns).Create(ctx, env, metav1.CreateOptions{}); err != nil { + writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("create environment %q: %v", envName, err)) + return + } + } else if err != nil { + writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("check environment: %v", err)) + return + } + req.Environment = envName + } + if req.Name == "" || req.Environment == "" || req.Code == "" { - writeJSONError(w, http.StatusBadRequest, "name, environment and code are required") + writeJSONError(w, http.StatusBadRequest, "name, environment/language and code are required") return } if req.Entrypoint == "" { @@ -237,15 +277,30 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) { methodValues = append(methodValues, method) } - literal := base64.StdEncoding.EncodeToString([]byte(req.Code)) - pkg := &unstructured.Unstructured{Object: map[string]any{ - "apiVersion": "fission.io/v1", - "kind": "Package", - "metadata": map[string]any{ - "name": pkgName, - "namespace": s.ns, - }, - "spec": map[string]any{ + // Build the package spec: Go uses source archive (builder), others use literal deployment + var pkgSpec map[string]any + if req.Language == "go" { + srcZip, err := s.buildGoSourceZip(req.Code) + if err != nil { + writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("build go source archive: %v", err)) + return + } + literal := base64.StdEncoding.EncodeToString(srcZip) + pkgSpec = map[string]any{ + "source": map[string]any{ + "type": "literal", + "literal": literal, + }, + "deployment": map[string]any{}, + "environment": map[string]any{ + "name": req.Environment, + "namespace": s.ns, + }, + "buildcommand": "build", + } + } else { + literal := base64.StdEncoding.EncodeToString([]byte(req.Code)) + pkgSpec = map[string]any{ "deployment": map[string]any{ "type": "literal", "literal": literal, @@ -255,7 +310,17 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) { "namespace": s.ns, }, "source": map[string]any{}, + } + } + + pkg := &unstructured.Unstructured{Object: map[string]any{ + "apiVersion": "fission.io/v1", + "kind": "Package", + "metadata": map[string]any{ + "name": pkgName, + "namespace": s.ns, }, + "spec": pkgSpec, }} if _, err := s.dyn.Resource(packageGVR).Namespace(s.ns).Create(ctx, pkg, metav1.CreateOptions{}); err != nil { @@ -325,6 +390,58 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) { }) } +func (s *server) buildLangEnvironment(name string, def langEnvDef) *unstructured.Unstructured { + spec := map[string]any{ + "version": int64(3), + "runtime": map[string]any{ + "image": def.Image, + }, + "poolsize": int64(1), + } + if def.BuilderImage != "" { + spec["builder"] = map[string]any{ + "image": def.BuilderImage, + "command": "build", + } + } + return &unstructured.Unstructured{Object: map[string]any{ + "apiVersion": "fission.io/v1", + "kind": "Environment", + "metadata": map[string]any{ + "name": name, + "namespace": s.ns, + }, + "spec": spec, + }} +} + +func (s *server) buildGoSourceZip(code string) ([]byte, error) { + var buf bytes.Buffer + zw := zip.NewWriter(&buf) + + fw, err := zw.Create("handler.go") + if err != nil { + return nil, err + } + if _, err := fw.Write([]byte(code)); err != nil { + return nil, err + } + + goMod := "module github.com/user/fn\n\ngo 1.23\n" + fw2, err := zw.Create("go.mod") + if err != nil { + return nil, err + } + if _, err := fw2.Write([]byte(goMod)); err != nil { + return nil, err + } + + if err := zw.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + func (s *server) handleGetFunction(w http.ResponseWriter, r *http.Request, name string) { ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() @@ -851,7 +968,7 @@ func decodeZipSource(zipBytes []byte) (string, error) { return "", err } - preferred := []string{"main.py", "main.js", "main.go"} + preferred := []string{"main.py", "main.js", "main.go", "handler.go", "handler.js", "handler.py"} for _, name := range preferred { for _, file := range reader.File { if strings.EqualFold(file.Name, name) { diff --git a/console/ui/index.html b/console/ui/index.html index 5dfc5b9..86e7a48 100644 --- a/console/ui/index.html +++ b/console/ui/index.html @@ -4,7 +4,7 @@ NUBES Fission Console - +