From e40e1e73958cd70d3c7f6198ab029738d65451a4 Mon Sep 17 00:00:00 2001
From: Garet Halliday <me@garet.holiday>
Date: Tue, 26 Sep 2023 19:18:53 -0500
Subject: [PATCH] hmmm

---
 Caddyfile                                     |  7 +---
 contrib/caddy/module.go                       |  8 ++++
 contrib/caddy/pggat.go                        | 38 ++++++++++++++++++-
 contrib/caddy/pgtype.go                       | 10 ++++-
 contrib/caddy/server.go                       |  3 +-
 lib/gat/module.go                             | 22 ++++++++++-
 lib/gat/modules/cloud_sql_discovery/module.go | 13 +++++++
 .../modules/digitalocean_discovery/module.go  | 13 +++++++
 lib/gat/modules/discovery/module.go           | 15 +++++++-
 lib/gat/modules/net_listener/module.go        | 13 ++++++-
 lib/gat/modules/pgbouncer/module.go           | 15 +++++++-
 lib/gat/modules/raw_pools/module.go           | 13 ++++++-
 lib/gat/modules/zalando/module.go             | 13 +++++++
 .../zalando_operator_discovery/module.go      | 13 +++++++
 14 files changed, 179 insertions(+), 17 deletions(-)
 create mode 100644 contrib/caddy/module.go

diff --git a/Caddyfile b/Caddyfile
index 7e04157c..ee28e678 100644
--- a/Caddyfile
+++ b/Caddyfile
@@ -4,10 +4,5 @@
 		"private": false,
 		"pool_mode": "transaction"
 	}
-}
-
-admin@:6432 {
-	pgbouncer {
-		"foo": "bar"
-	}
+	listen tcp :5432
 }
diff --git a/contrib/caddy/module.go b/contrib/caddy/module.go
new file mode 100644
index 00000000..433ddb54
--- /dev/null
+++ b/contrib/caddy/module.go
@@ -0,0 +1,8 @@
+package caddy
+
+import "encoding/json"
+
+type ServerModule struct {
+	Type   string          `json:"type"`
+	Config json.RawMessage `json:"config,omitempty"`
+}
diff --git a/contrib/caddy/pggat.go b/contrib/caddy/pggat.go
index 0aac54d1..1513bcea 100644
--- a/contrib/caddy/pggat.go
+++ b/contrib/caddy/pggat.go
@@ -1,7 +1,11 @@
 package caddy
 
 import (
+	"fmt"
+
 	"github.com/caddyserver/caddy/v2"
+
+	"gfx.cafe/gfx/pggat/lib/gat"
 )
 
 func init() {
@@ -10,6 +14,8 @@ func init() {
 
 type PGGat struct {
 	Servers []Server `json:"servers,omitempty"`
+
+	servers []*gat.Server
 }
 
 func (*PGGat) CaddyModule() caddy.ModuleInfo {
@@ -21,15 +27,43 @@ func (*PGGat) CaddyModule() caddy.ModuleInfo {
 	}
 }
 
+func (T *PGGat) Provision(ctx caddy.Context) error {
+	T.servers = make([]*gat.Server, 0, len(T.Servers))
+	for _, server := range T.Servers {
+		var modules []gat.Module
+		for _, module := range server.Modules {
+			info, ok := gat.GetModule(module.Type)
+			if !ok {
+				return fmt.Errorf("module not found: %s", module.Type)
+			}
+			modules = append(modules, info.New())
+		}
+
+		T.servers = append(T.servers, gat.NewServer(modules...))
+	}
+
+	return nil
+}
+
 func (T *PGGat) Start() error {
-	// TODO(garet)
+	for _, server := range T.servers {
+		if err := server.Start(); err != nil {
+			return err
+		}
+	}
 	return nil
 }
 
 func (T *PGGat) Stop() error {
-	// TODO(garet)
+	for _, server := range T.servers {
+		if err := server.Stop(); err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 
 var _ caddy.Module = (*PGGat)(nil)
 var _ caddy.App = (*PGGat)(nil)
+var _ caddy.Provisioner = (*PGGat)(nil)
diff --git a/contrib/caddy/pgtype.go b/contrib/caddy/pgtype.go
index 0e666a42..0d840f19 100644
--- a/contrib/caddy/pgtype.go
+++ b/contrib/caddy/pgtype.go
@@ -6,6 +6,8 @@ import (
 	"github.com/caddyserver/caddy/v2"
 	"github.com/caddyserver/caddy/v2/caddyconfig"
 	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+
+	"gfx.cafe/gfx/pggat/lib/gat"
 )
 
 func init() {
@@ -22,7 +24,13 @@ func (ServerType) Setup(blocks []caddyfile.ServerBlock, m map[string]any) (*cadd
 
 	for _, block := range blocks {
 		var server Server
-		// TODO(garet) populate server
+		for _, segment := range block.Segments {
+			if info, ok := gat.GetModule(segment.Directive()); ok {
+				server.Modules = append(server.Modules, ServerModule{
+					Type: info.ID,
+				})
+			}
+		}
 
 		server.Listen = make([]ServerSlug, 0, len(block.Keys))
 		for _, key := range block.Keys {
diff --git a/contrib/caddy/server.go b/contrib/caddy/server.go
index 21c28d0d..37f7dfbc 100644
--- a/contrib/caddy/server.go
+++ b/contrib/caddy/server.go
@@ -1,5 +1,6 @@
 package caddy
 
 type Server struct {
-	Listen []ServerSlug `json:"listen,omitempty"`
+	Listen  []ServerSlug   `json:"listen,omitempty"`
+	Modules []ServerModule `json:"modules,omitempty"`
 }
diff --git a/lib/gat/module.go b/lib/gat/module.go
index 0ec77c16..cdd86365 100644
--- a/lib/gat/module.go
+++ b/lib/gat/module.go
@@ -1,5 +1,25 @@
 package gat
 
+type ModuleInfo struct {
+	ID  string
+	New func() Module
+}
+
 type Module interface {
-	GatModule()
+	GatModule() ModuleInfo
+}
+
+var modules map[string]ModuleInfo
+
+func RegisterModule(module Module) {
+	if modules == nil {
+		modules = make(map[string]ModuleInfo)
+	}
+	info := module.GatModule()
+	modules[info.ID] = info
+}
+
+func GetModule(id string) (ModuleInfo, bool) {
+	info, ok := modules[id]
+	return info, ok
 }
diff --git a/lib/gat/modules/cloud_sql_discovery/module.go b/lib/gat/modules/cloud_sql_discovery/module.go
index 0e595be5..efdf78ce 100644
--- a/lib/gat/modules/cloud_sql_discovery/module.go
+++ b/lib/gat/modules/cloud_sql_discovery/module.go
@@ -10,12 +10,25 @@ import (
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
 )
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	Config
 
 	discovery.Module `json:"-"`
 }
 
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "cloud_sql_discovery",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
+
 func (T *Module) Start() error {
 	d, err := NewDiscoverer(T.Config)
 	if err != nil {
diff --git a/lib/gat/modules/digitalocean_discovery/module.go b/lib/gat/modules/digitalocean_discovery/module.go
index 2e1cb43e..2f4635e4 100644
--- a/lib/gat/modules/digitalocean_discovery/module.go
+++ b/lib/gat/modules/digitalocean_discovery/module.go
@@ -10,12 +10,25 @@ import (
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
 )
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	Config
 
 	discovery.Module `json:"-"`
 }
 
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "digitalocean_discovery",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
+
 func (T *Module) Start() error {
 	d, err := NewDiscoverer(T.Config)
 	if err != nil {
diff --git a/lib/gat/modules/discovery/module.go b/lib/gat/modules/discovery/module.go
index dc7d799b..d52f03bc 100644
--- a/lib/gat/modules/discovery/module.go
+++ b/lib/gat/modules/discovery/module.go
@@ -19,6 +19,10 @@ import (
 	"gfx.cafe/gfx/pggat/lib/util/slices"
 )
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	Config
 
@@ -31,6 +35,15 @@ type Module struct {
 	mu    sync.RWMutex
 }
 
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "discovery",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
+
 func (T *Module) Start() error {
 	if T.closed != nil {
 		return errors.New("start called multiple times")
@@ -482,8 +495,6 @@ func (T *Module) removePool(user, database string) {
 	T.pools.Delete(user, database)
 }
 
-func (T *Module) GatModule() {}
-
 func (T *Module) ReadMetrics(metrics *metrics.Pools) {
 	T.mu.RLock()
 	defer T.mu.RUnlock()
diff --git a/lib/gat/modules/net_listener/module.go b/lib/gat/modules/net_listener/module.go
index e214c1c1..734f6b96 100644
--- a/lib/gat/modules/net_listener/module.go
+++ b/lib/gat/modules/net_listener/module.go
@@ -11,6 +11,10 @@ import (
 	"gfx.cafe/gfx/pggat/lib/gat"
 )
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	Config
 
@@ -19,7 +23,14 @@ type Module struct {
 	accepted chan<- gat.AcceptedConn
 }
 
-func (*Module) GatModule() {}
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "net_listener",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
 
 func (T *Module) Start() error {
 	if T.listener != nil {
diff --git a/lib/gat/modules/pgbouncer/module.go b/lib/gat/modules/pgbouncer/module.go
index e12ed7bf..67a645c1 100644
--- a/lib/gat/modules/pgbouncer/module.go
+++ b/lib/gat/modules/pgbouncer/module.go
@@ -32,6 +32,10 @@ type authQueryResult struct {
 	Password string `sql:"1"`
 }
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	Config
 
@@ -42,6 +46,15 @@ type Module struct {
 	unixListener net_listener.Module
 }
 
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "pgbouncer",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
+
 func (T *Module) Start() error {
 	trackedParameters := append([]strutil.CIString{
 		strutil.MakeCIString("client_encoding"),
@@ -355,8 +368,6 @@ func (T *Module) Listen(ch chan<- gat.AcceptedConn) {
 	T.unixListener.Listen(ch)
 }
 
-func (T *Module) GatModule() {}
-
 var _ gat.Module = (*Module)(nil)
 var _ gat.Provider = (*Module)(nil)
 var _ gat.Listener = (*Module)(nil)
diff --git a/lib/gat/modules/raw_pools/module.go b/lib/gat/modules/raw_pools/module.go
index 5272591d..1ef16204 100644
--- a/lib/gat/modules/raw_pools/module.go
+++ b/lib/gat/modules/raw_pools/module.go
@@ -9,12 +9,23 @@ import (
 	"gfx.cafe/gfx/pggat/lib/util/maps"
 )
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	pools maps.TwoKey[string, string, *pool.Pool]
 	mu    sync.RWMutex
 }
 
-func (T *Module) GatModule() {}
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "raw_pools",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
 
 func (T *Module) Add(user, database string, p *pool.Pool) {
 	T.mu.Lock()
diff --git a/lib/gat/modules/zalando/module.go b/lib/gat/modules/zalando/module.go
index b9e79ea0..7c285328 100644
--- a/lib/gat/modules/zalando/module.go
+++ b/lib/gat/modules/zalando/module.go
@@ -9,12 +9,25 @@ import (
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
 )
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	Config
 
 	pgbouncer.Module `json:"-"`
 }
 
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "zalando",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
+
 func (T *Module) Start() error {
 	pgb := pgbouncer.Default
 	if pgb.Databases == nil {
diff --git a/lib/gat/modules/zalando_operator_discovery/module.go b/lib/gat/modules/zalando_operator_discovery/module.go
index 24667006..22d1d34c 100644
--- a/lib/gat/modules/zalando_operator_discovery/module.go
+++ b/lib/gat/modules/zalando_operator_discovery/module.go
@@ -10,12 +10,25 @@ import (
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
 )
 
+func init() {
+	gat.RegisterModule((*Module)(nil))
+}
+
 type Module struct {
 	Config
 
 	discovery.Module `json:"-"`
 }
 
+func (*Module) GatModule() gat.ModuleInfo {
+	return gat.ModuleInfo{
+		ID: "zalando_operator_discovery",
+		New: func() gat.Module {
+			return new(Module)
+		},
+	}
+}
+
 func (T *Module) Start() error {
 	d, err := NewDiscoverer(T.Config)
 	if err != nil {
-- 
GitLab