feat: notes-python CRUD example + runtime path/query forwarding
- invoke.go: forward sub-path and query string to function pods - server.js v0.1.2: add _path, _query, _method to event - server.py v0.1.1: add _path, _query, _method to event - upload.go: bump runtime versions (nodejs20:v0.1.2, python3.11:v0.1.1) - examples/notes-python: CRUD notes via sub-path routing - sql-runner: generic SQL executor for DDL jobs - notes: CRUD router (/add, /update, /delete) - notes-list: SELECT all notes - init.tf: create TABLE + INDEX on apply
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
// Изменено: 2026-03-07
|
||||
// Изменено: 2026-03-09
|
||||
// invoke.go — прокси-обработчик для вызова HTTP-триггеров функций.
|
||||
// Маршрут: ANY /fn/{namespace}/{name}
|
||||
// Маршрут: ANY /fn/{namespace}/{name} и /fn/{namespace}/{name}/**
|
||||
// Не защищён auth-токеном — это публичный эндпоинт для вызова функций.
|
||||
// Проксирует запрос к ClusterIP Service функции внутри кластера:
|
||||
// http://{name}.sless-fn-{namespace}.svc.cluster.local:8080
|
||||
// Функция должна быть задеплоена (HTTP триггер активен).
|
||||
// Sub-path и query string пробрасываются как есть:
|
||||
// /fn/ns/notes/add?title=x → http://notes.sless-fn-ns.svc.../add?title=x
|
||||
|
||||
package handler
|
||||
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
@@ -23,15 +25,24 @@ var httpClient = &http.Client{Timeout: 30 * time.Second}
|
||||
|
||||
// InvokeFunction проксирует входящий запрос к Service функции в кластере.
|
||||
// Namespace выбирается из пути, имя функции — тоже из пути.
|
||||
// Сохраняет метод, тело, и Content-Type заголовок оригинального запроса.
|
||||
// Сохраняет метод, тело, Content-Type, sub-path и query string.
|
||||
func (h *Handler) InvokeFunction(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
ns := vars["namespace"]
|
||||
name := vars["name"]
|
||||
|
||||
// Вычисляем sub-path после /fn/{namespace}/{name}
|
||||
// Например: /fn/default/notes/add → subPath = /add
|
||||
prefix := "/fn/" + ns + "/" + name
|
||||
subPath := strings.TrimPrefix(r.URL.Path, prefix)
|
||||
|
||||
// Внутренний URL к Service функции (DNS внутри кластера)
|
||||
// Namespace функций: sless-fn-{user-namespace}
|
||||
target := fmt.Sprintf("http://%s.sless-fn-%s.svc.cluster.local:8080", name, ns)
|
||||
target := fmt.Sprintf("http://%s.sless-fn-%s.svc.cluster.local:8080%s", name, ns, subPath)
|
||||
|
||||
// Пробрасываем query string если есть
|
||||
if r.URL.RawQuery != "" {
|
||||
target += "?" + r.URL.RawQuery
|
||||
}
|
||||
|
||||
// Создаём проксируемый запрос с тем же методом и телом
|
||||
proxyReq, err := http.NewRequestWithContext(r.Context(), r.Method, target, r.Body)
|
||||
|
||||
@@ -31,9 +31,9 @@ import (
|
||||
func runtimeBaseImage(runtime string) (string, error) {
|
||||
switch runtime {
|
||||
case "python3.11":
|
||||
return "naeel/sless-runtime-python3.11:v0.1.0", nil
|
||||
return "naeel/sless-runtime-python3.11:v0.1.1", nil
|
||||
case "nodejs20":
|
||||
return "naeel/sless-runtime-nodejs20:v0.1.1", nil
|
||||
return "naeel/sless-runtime-nodejs20:v0.1.2", nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported runtime: %q (supported: python3.11, nodejs20)", runtime)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user