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:
26
pluginHost/README.md
Normal file
26
pluginHost/README.md
Normal 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
26
pluginHost/go.mod
Normal 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
51
pluginHost/go.sum
Normal 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
145
pluginHost/main.go
Normal 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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user