144 lines
3.8 KiB
Go
144 lines
3.8 KiB
Go
package invoke
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.oblat.lv/alex/triggerssmith/internal/config"
|
|
"git.oblat.lv/alex/triggerssmith/internal/worker"
|
|
"github.com/go-chi/chi/v5"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type Function struct {
|
|
ID uint `gorm:"primaryKey;autoIncrement"`
|
|
FunctionName string `gorm:"not null"`
|
|
Version string `gorm:"not null"`
|
|
Path string `gorm:"not null"`
|
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
|
}
|
|
|
|
type TerminalLogger struct {
|
|
fc *worker.FuncConfig
|
|
}
|
|
|
|
func (l *TerminalLogger) Write(line string) {
|
|
slog.Warn("function stderr", slog.String("line", line), slog.String("n:v", fmt.Sprintf("%s:%s", l.fc.Name, l.fc.Version)))
|
|
}
|
|
|
|
type JSONFileLogger struct {
|
|
fc *worker.FuncConfig
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func NewJSONFileLogger(fc *worker.FuncConfig, path string) (*JSONFileLogger, error) {
|
|
dir := filepath.Dir(path)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
handler := slog.NewJSONHandler(file, &slog.HandlerOptions{})
|
|
logger := slog.New(handler)
|
|
|
|
return &JSONFileLogger{
|
|
fc: fc,
|
|
logger: logger,
|
|
}, nil
|
|
}
|
|
|
|
func (l *JSONFileLogger) Write(line string) {
|
|
l.logger.Warn("function stderr",
|
|
slog.String("function", l.fc.Name),
|
|
slog.String("version", l.fc.Version),
|
|
slog.String("line", line),
|
|
)
|
|
}
|
|
|
|
func InvokeHandler(cfg *config.Config) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
id := chi.URLParam(r, "function_id")
|
|
version := chi.URLParam(r, "function_version")
|
|
slog.Debug("executing a function", slog.String("id", id), slog.String("version", version))
|
|
root := cfg.Functions.FunctionDir
|
|
treeCfg, _ := worker.LoadTreeConfig(root)
|
|
db, err := worker.OpenDB(treeCfg, root)
|
|
if err != nil {
|
|
slog.Error("Failed to open db", slog.String("err", err.Error()))
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
//f, _ := worker.FindFunction(db, "echo", "0.0.1-00130112025")
|
|
f, err := worker.FindFunction(db, id, version)
|
|
if err != nil {
|
|
slog.Error("Failed to find function", slog.String("err", err.Error()))
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
fc, err := worker.LoadFunctionConfig(root, f.FunctionName, f.Path)
|
|
if err != nil {
|
|
slog.Error("Failed to load function config", slog.String("err", err.Error()))
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var logger worker.Logger
|
|
switch fc.Log.Output {
|
|
case "stdout":
|
|
logger = &TerminalLogger{
|
|
fc: fc,
|
|
}
|
|
case "file":
|
|
fileLogger, err := NewJSONFileLogger(fc, filepath.Join(treeCfg.Log.Path, fmt.Sprintf("%s:%s", fc.Name, f.Path), "event.log.json"))
|
|
if err != nil {
|
|
slog.Error("Failed to create file logger", slog.String("err", err.Error()))
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
logger = fileLogger
|
|
}
|
|
|
|
var frmt = func(s1 string, s2 string) string {
|
|
return fmt.Sprintf("FAAS_%s=%s", s1, s2)
|
|
}
|
|
var env = []string{
|
|
frmt("PROTOCOL", r.Proto),
|
|
|
|
frmt("METHOD", r.Method),
|
|
frmt("PATH", r.URL.Path),
|
|
frmt("QUERY", r.URL.RawQuery),
|
|
}
|
|
|
|
for k, v := range r.Header {
|
|
key := "FAAS_HEADER_" + strings.ReplaceAll(k, "-", "_")
|
|
env = append(env, key+"="+v[0])
|
|
}
|
|
|
|
input, _ := io.ReadAll(r.Body)
|
|
path := filepath.Join(root, f.FunctionName, f.Path, fc.Entry)
|
|
output, err := worker.RunFunction(&worker.RunOps{
|
|
Path: path,
|
|
FuncConfig: fc,
|
|
Log: logger,
|
|
Env: env,
|
|
}, input)
|
|
if err != nil {
|
|
slog.Error("Failed to run function", slog.String("err", err.Error()))
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
slog.Debug("executing done", slog.Any("in", input), slog.Any("out", output))
|
|
w.Write(output)
|
|
}
|
|
}
|