add plugin hosts to seperate plugins from gloomi

This adds a plugin host that seperates plugins from the gloomi process,
allowing for plugins to be unloaded and loaded. This commit also has a
fair amount of other changes, nice to haves and bug fixes, some notable
changes are:
- Highly available reverse proxy from my Flux project
- Improved gloomi functionality
This commit is contained in:
Zoe
2025-05-14 19:31:58 -05:00
parent ad0e949070
commit b8f5bce66c
18 changed files with 852 additions and 239 deletions

26
pluginHost/README.md Normal file
View File

@@ -0,0 +1,26 @@
# Plugin Host
This is the plugin host for GLoom. This is a small program that is responsible for loading and managing plugins. It is responsible for starting the plugin and forwarding requests to it. This is meant to be used with GLoom, but can be used as a standalone program if you so choose. The Plugin Host is built automatically when you build GLoom via `zqdgr build`.
## Building
To build the plugin host standalone, run the following command in the `pluginHost` directory:
```bash
zqdgr build
```
or run the following command in the project root:
```bash
zqdgr build:pluginHost
```
## Running
To run the plugin host, run the following command:
```bash
./host <pluginPath> <socketPath> [controlPath]
```
- `pluginPath` - The path to the plugin to load.
- `socketPath` - The path to the socket that the plugin will use to listen for http requests through.
- `controlPath` - (Optional) The path to the control socket. If not provided, the host will not send errors or status messages to the control socket and instead log them to stdout and stderr.

26
pluginHost/go.mod Normal file
View File

@@ -0,0 +1,26 @@
module github.com/juls0730/gloom/pluginHost
go 1.24.2
require github.com/gofiber/fiber/v3 v3.0.0-beta.4
require (
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gofiber/schema v1.2.0 // indirect
github.com/gofiber/utils/v2 v2.0.0-beta.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/tinylib/msgp v1.2.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.58.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
)

51
pluginHost/go.sum Normal file
View File

@@ -0,0 +1,51 @@
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gofiber/fiber/v3 v3.0.0-beta.4 h1:KzDSavvhG7m81NIsmnu5l3ZDbVS4feCidl4xlIfu6V0=
github.com/gofiber/fiber/v3 v3.0.0-beta.4/go.mod h1:/WFUoHRkZEsGHyy2+fYcdqi109IVOFbVwxv1n1RU+kk=
github.com/gofiber/schema v1.2.0 h1:j+ZRrNnUa/0ZuWrn/6kAtAufEr4jCJ+JuTURAMxNSZg=
github.com/gofiber/schema v1.2.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
github.com/gofiber/utils/v2 v2.0.0-beta.7 h1:NnHFrRHvhrufPABdWajcKZejz9HnCWmT/asoxRsiEbQ=
github.com/gofiber/utils/v2 v2.0.0-beta.7/go.mod h1:J/M03s+HMdZdvhAeyh76xT72IfVqBzuz/OJkrMa7cwU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE=
github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

145
pluginHost/main.go Normal file
View File

@@ -0,0 +1,145 @@
package main
import (
"fmt"
"io"
"log"
"net"
"os"
"os/signal"
"path/filepath"
"plugin"
"github.com/gofiber/fiber/v3"
)
var pluginPath string
var socketPath string
var controlPath string
func init() {
if len(os.Args) < 3 {
fmt.Fprintf(os.Stderr, "Usage: pluginHost <pluginPath> <socketPath>")
os.Exit(1)
}
pluginPath = os.Args[1]
socketPath = os.Args[2]
if len(os.Args) > 3 {
controlPath = os.Args[3]
}
}
type Plugin interface {
Init() (*fiber.Config, error)
RegisterRoutes(app fiber.Router)
// Name() string
}
type PluginInstance struct {
Plugin Plugin
Name string
Path string
Router *fiber.App
}
// Init is the entry point for a container process
func (p *PluginInstance) Run(pluginName string) {
log.Printf("Starting container with plugin %s", pluginName)
// Load and initialize the plugin here
}
func main() {
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
<-signalChan
// TODO: maybe do something graceful here
fmt.Println("Received SIGINT, shutting down...")
os.Exit(0)
}()
var writer io.Writer
writer = os.Stderr
if controlPath != "" {
fmt.Printf("Waiting for control connection on %s\n", controlPath)
controlListener, err := net.Listen("unix", controlPath)
if err != nil {
log.Fatalf("Error listening on control socket: %v", err)
}
defer controlListener.Close()
conn, err := controlListener.Accept()
if err != nil {
log.Printf("Error accepting control connection: %v", err)
return
}
defer conn.Close()
var ok bool
writer, ok = conn.(io.Writer)
if !ok {
log.Printf("Control connection is not a writer")
return
}
}
if _, err := os.Stat(socketPath); err == nil {
fmt.Fprintf(writer, "Error: Socket %s already exists\n", socketPath)
os.Exit(1)
}
realPluginPath, err := filepath.Abs(pluginPath)
if err != nil {
fmt.Fprintf(writer, "Error: could not get absolute plugin path: %v\n", err)
os.Exit(1)
}
p, err := plugin.Open(realPluginPath)
if err != nil {
fmt.Fprintf(writer, "Error: could not open plugin %s: %v\n", realPluginPath, err)
os.Exit(1)
}
symbol, err := p.Lookup("Plugin")
if err != nil {
fmt.Fprintf(writer, "Error: could not find 'Plugin' symbol in %s: %v\n", realPluginPath, err)
os.Exit(1)
}
pluginLib, ok := symbol.(Plugin)
if !ok {
fmt.Fprintf(writer, "Error: symbol 'Plugin' in %s is not a Plugin interface\n", realPluginPath)
os.Exit(1)
}
pluginConfig, err := pluginLib.Init()
if err != nil {
fmt.Fprintf(writer, "Error: error initializing plugin %s: %v\n", realPluginPath, err)
os.Exit(1)
}
config := fiber.Config{}
if pluginConfig != nil {
config = *pluginConfig
}
router := fiber.New(config)
pluginLib.RegisterRoutes(router)
// listen for connections on the socket
listener, err := net.Listen("unix", socketPath)
if err != nil {
fmt.Fprintf(writer, "Error: error listening on socket %s: %v\n", socketPath, err)
os.Exit(1)
}
fmt.Fprintf(writer, "ready\n")
// technically this can still error
router.Listener(listener, fiber.ListenConfig{
DisableStartupMessage: true,
})
}