Files
zeekay 42520800f8 feat(base): add OpenFaaS functions plugin
Per-tenant serverless function management via OpenFaaS gateway:
- CRUD at /api/functions (deploy, list, get, delete)
- Invocation at /api/functions/{name}/invoke (sync + async)
- Tenant isolation via t-{slug}-{name} namespacing
- _functions system collection for tracking
- Auto-injected env: IAM_URL, STORAGE_URL
2026-03-01 18:38:16 -08:00

98 lines
2.4 KiB
Go

// Package functions implements serverless function management for Hanzo Base
// via OpenFaaS. It provides per-tenant function deployment, invocation, and
// lifecycle management through the Base API surface.
//
// Example:
//
// functions.MustRegister(app, functions.Config{
// GatewayURL: "http://openfaas-gateway.hanzo.svc:8080",
// FunctionNamespace: "openfaas-fn",
// RegistryURL: "registry.hanzo.svc:5000",
// })
package functions
import (
"net/http"
"github.com/hanzoai/base/core"
"github.com/hanzoai/base/tools/router"
)
// Config holds the Functions plugin configuration.
type Config struct {
// GatewayURL is the OpenFaaS gateway URL.
GatewayURL string
// FunctionNamespace is the K8s namespace for function deployments.
FunctionNamespace string
// RegistryURL is the container registry URL for function images.
RegistryURL string
}
// MustRegister registers the Functions plugin and panics on failure.
func MustRegister(app core.App, config Config) {
if err := Register(app, config); err != nil {
panic(err)
}
}
// Register registers the Functions plugin to the provided app instance.
func Register(app core.App, config Config) error {
if config.GatewayURL == "" {
config.GatewayURL = "http://openfaas-gateway.hanzo.svc:8080"
}
if config.FunctionNamespace == "" {
config.FunctionNamespace = "openfaas-fn"
}
if config.RegistryURL == "" {
config.RegistryURL = "registry.hanzo.svc:5000"
}
p := &plugin{
app: app,
config: config,
client: &http.Client{},
}
app.OnBootstrap().BindFunc(func(e *core.BootstrapEvent) error {
if err := e.Next(); err != nil {
return err
}
return p.ensureCollections()
})
app.OnServe().BindFunc(func(e *core.ServeEvent) error {
p.registerRoutes(e.Router)
return e.Next()
})
return nil
}
type plugin struct {
app core.App
config Config
client *http.Client
}
// --------------------------------------------------------------------------
// Routes
// --------------------------------------------------------------------------
func (p *plugin) registerRoutes(r *router.Router[*core.RequestEvent]) {
api := r.Group("/api/functions")
// Function CRUD
api.POST("", p.handleDeployFunction)
api.GET("", p.handleListFunctions)
api.GET("/{name}", p.handleGetFunction)
api.DELETE("/{name}", p.handleDeleteFunction)
// Invocation
api.POST("/{name}/invoke", p.handleInvokeFunction)
// Logs
api.GET("/{name}/logs", p.handleGetLogs)
}