From 465e8b273b1f25b601f6adf53f53e51253368959 Mon Sep 17 00:00:00 2001
From: Garet Halliday <me@garet.holiday>
Date: Fri, 29 Sep 2023 18:54:36 -0500
Subject: [PATCH] pgbouncer-spilo working

---
 cmd/caddygat/pgbouncer.go                     |  6 +-
 cmd/caddygat/pgbouncer_spilo.go               | 59 +++++++++++++++++++
 go.mod                                        |  4 ++
 go.sum                                        |  9 +++
 lib/gat/handlers/pgbouncer_spilo/config.go    | 44 ++++++++++++++
 .../{zalando => pgbouncer_spilo}/module.go    | 16 ++---
 lib/gat/handlers/zalando/config.go            | 16 -----
 lib/gat/standard/standard.go                  |  2 +-
 8 files changed, 127 insertions(+), 29 deletions(-)
 create mode 100644 cmd/caddygat/pgbouncer_spilo.go
 create mode 100644 lib/gat/handlers/pgbouncer_spilo/config.go
 rename lib/gat/handlers/{zalando => pgbouncer_spilo}/module.go (91%)
 delete mode 100644 lib/gat/handlers/zalando/config.go

diff --git a/cmd/caddygat/pgbouncer.go b/cmd/caddygat/pgbouncer.go
index 74b1db59..4fe2f957 100644
--- a/cmd/caddygat/pgbouncer.go
+++ b/cmd/caddygat/pgbouncer.go
@@ -18,13 +18,11 @@ func init() {
 		Name:  "pgbouncer",
 		Usage: "<config file>",
 		Short: "Runs in pgbouncer compatibility mode",
-		Func: func(flags caddycmd.Flags) (int, error) {
-			return runPgBouncer(flags)
-		},
+		Func:  runPgbouncer,
 	})
 }
 
-func runPgBouncer(flags caddycmd.Flags) (int, error) {
+func runPgbouncer(flags caddycmd.Flags) (int, error) {
 	caddy.TrapSignals()
 
 	file := flags.Arg(0)
diff --git a/cmd/caddygat/pgbouncer_spilo.go b/cmd/caddygat/pgbouncer_spilo.go
new file mode 100644
index 00000000..306615d9
--- /dev/null
+++ b/cmd/caddygat/pgbouncer_spilo.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+	"time"
+
+	"github.com/caddyserver/caddy/v2"
+	"github.com/caddyserver/caddy/v2/caddyconfig"
+	caddycmd "github.com/caddyserver/caddy/v2/cmd"
+
+	"gfx.cafe/gfx/pggat/lib/gat"
+	"gfx.cafe/gfx/pggat/lib/gat/handlers/pgbouncer_spilo"
+	"gfx.cafe/gfx/pggat/lib/util/dur"
+
+	"gfx.cafe/util/go/gun"
+)
+
+func init() {
+	caddycmd.RegisterCommand(caddycmd.Command{
+		Name:  "pgbouncer-spilo",
+		Short: "Runs in pgbouncer-spilo compatibility mode",
+		Func:  runPgbouncerSpilo,
+	})
+}
+
+func runPgbouncerSpilo(flags caddycmd.Flags) (int, error) {
+	caddy.TrapSignals()
+
+	var config pgbouncer_spilo.Config
+	gun.Load(&config)
+
+	var pggat gat.Config
+	pggat.StatLogPeriod = dur.Duration(1 * time.Minute)
+
+	var server gat.ServerConfig
+	server.Listen = config.Listen()
+	server.Routes = append(server.Routes, gat.RouteConfig{
+		Handle: caddyconfig.JSONModuleObject(
+			pgbouncer_spilo.Module{
+				Config: config,
+			},
+			"handler",
+			"pgbouncer_spilo",
+			nil,
+		),
+	})
+	pggat.Servers = append(pggat.Servers, server)
+
+	caddyConfig := caddy.Config{
+		AppsRaw: caddy.ModuleMap{
+			"pggat": caddyconfig.JSON(pggat, nil),
+		},
+	}
+
+	if err := caddy.Run(&caddyConfig); err != nil {
+		return caddy.ExitCodeFailedStartup, err
+	}
+
+	select {}
+}
diff --git a/go.mod b/go.mod
index 706ee3ef..ac588dfd 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.20
 
 require (
 	gfx.cafe/ghalliday1/scram v0.0.2
+	gfx.cafe/util/go/gun v0.0.0-20230721185457-c559e86c829c
 	github.com/caddyserver/caddy/v2 v2.7.4
 	github.com/digitalocean/godo v1.102.1
 	github.com/google/uuid v1.3.0
@@ -22,6 +23,8 @@ require (
 	github.com/caddyserver/certmagic v0.19.2 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
+	github.com/cristalhq/aconfig v0.18.3 // indirect
+	github.com/cristalhq/aconfig/aconfigdotenv v0.17.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/emicklei/go-restful/v3 v3.9.0 // indirect
 	github.com/go-logr/logr v1.2.4 // indirect
@@ -45,6 +48,7 @@ require (
 	github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
 	github.com/imdario/mergo v0.3.12 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/joho/godotenv v1.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.5 // indirect
diff --git a/go.sum b/go.sum
index 91e67319..ba1b8f2a 100644
--- a/go.sum
+++ b/go.sum
@@ -37,6 +37,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 gfx.cafe/ghalliday1/scram v0.0.2 h1:uuScaL7DUEP/lKWJnA5kKHq5RJev26q8DMbP3gKviAg=
 gfx.cafe/ghalliday1/scram v0.0.2/go.mod h1:qt6+WJoFcX2id67G5w+/dktBJ56Se0sZAa7WjqfNNu0=
+gfx.cafe/util/go/gun v0.0.0-20230721185457-c559e86c829c h1:4XxKaHfYPam36FibTiy1Te7ycfW4+ys08WYyDih5VmU=
+gfx.cafe/util/go/gun v0.0.0-20230721185457-c559e86c829c/go.mod h1:zxq7FdmfdrI4oGeze0MPJt9WqdkFj3BDDSAWRuB63JQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -74,6 +76,11 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cristalhq/aconfig v0.17.0/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E=
+github.com/cristalhq/aconfig v0.18.3 h1:Or12LIWIF+2mQpcGWA2PQnNc55+WiHFAqRjYh/pQNtM=
+github.com/cristalhq/aconfig v0.18.3/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E=
+github.com/cristalhq/aconfig/aconfigdotenv v0.17.1 h1:HG2ql5fGe4FLL2fUv6o+o0YRyF1mWEcYkNfWGWD82k4=
+github.com/cristalhq/aconfig/aconfigdotenv v0.17.1/go.mod h1:gQIKkh+HkVcODvMNz/cLbH65Pk9b0r4tfolCOsI8G9I=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 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=
@@ -208,6 +215,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
+github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
diff --git a/lib/gat/handlers/pgbouncer_spilo/config.go b/lib/gat/handlers/pgbouncer_spilo/config.go
new file mode 100644
index 00000000..81dc3707
--- /dev/null
+++ b/lib/gat/handlers/pgbouncer_spilo/config.go
@@ -0,0 +1,44 @@
+package pgbouncer_spilo
+
+import (
+	"strconv"
+
+	"github.com/caddyserver/caddy/v2/caddyconfig"
+
+	"gfx.cafe/gfx/pggat/lib/gat"
+	"gfx.cafe/gfx/pggat/lib/gat/ssl/servers/x509_key_pair"
+)
+
+type Config struct {
+	Host                string `json:"host" env:"PGHOST"`
+	Port                int    `json:"port" env:"PGPORT"`
+	User                string `json:"user" env:"PGUSER"`
+	Schema              string `json:"schema" env:"PGSCHEMA"`
+	Password            string `json:"password" env:"PGPASSWORD"`
+	PoolerPort          int    `json:"pooler_port" env:"CONNECTION_POOLER_PORT"`
+	PoolerMode          string `json:"pooler_mode" env:"CONNECTION_POOLER_MODE"`
+	PoolerDefaultSize   int    `json:"pooler_default_size" env:"CONNECTION_POOLER_DEFAULT_SIZE"`
+	PoolerMinSize       int    `json:"pooler_min_size" env:"CONNECTION_POOLER_MIN_SIZE"`
+	PoolerReserveSize   int    `json:"pooler_reserve_size" env:"CONNECTION_POOLER_RESERVE_SIZE"`
+	PoolerMaxClientConn int    `json:"pooler_max_client_conn" env:"CONNECTION_POOLER_MAX_CLIENT_CONN"`
+	PoolerMaxDBConn     int    `json:"pooler_max_db_conn" env:"CONNECTION_POOLER_MAX_DB_CONN"`
+}
+
+func (T Config) Listen() []gat.ListenerConfig {
+	ssl := caddyconfig.JSONModuleObject(
+		x509_key_pair.Server{
+			CertFile: "/etc/ssl/certs/pgbouncer.crt",
+			KeyFile:  "/etc/ssl/certs/pgbouncer.key",
+		},
+		"provider",
+		"x509_key_pair",
+		nil,
+	)
+
+	return []gat.ListenerConfig{
+		{
+			Address: ":" + strconv.Itoa(T.PoolerPort),
+			SSL:     ssl,
+		},
+	}
+}
diff --git a/lib/gat/handlers/zalando/module.go b/lib/gat/handlers/pgbouncer_spilo/module.go
similarity index 91%
rename from lib/gat/handlers/zalando/module.go
rename to lib/gat/handlers/pgbouncer_spilo/module.go
index ff739eca..5a80a19b 100644
--- a/lib/gat/handlers/zalando/module.go
+++ b/lib/gat/handlers/pgbouncer_spilo/module.go
@@ -1,4 +1,4 @@
-package zalando
+package pgbouncer_spilo
 
 import (
 	"fmt"
@@ -25,7 +25,7 @@ type Module struct {
 
 func (*Module) CaddyModule() caddy.ModuleInfo {
 	return caddy.ModuleInfo{
-		ID: "pggat.handlers.zalando",
+		ID: "pggat.handlers.pgbouncer_spilo",
 		New: func() caddy.Module {
 			return new(Module)
 		},
@@ -38,19 +38,19 @@ func (T *Module) Provision(ctx caddy.Context) error {
 		pgb.Databases = make(map[string]pgbouncer.Database)
 	}
 	pgb.Databases["*"] = pgbouncer.Database{
-		Host:     T.PGHost,
-		Port:     T.PGPort,
-		AuthUser: T.PGUser,
+		Host:     T.Host,
+		Port:     T.Port,
+		AuthUser: T.User,
 	}
 	pgb.PgBouncer.PoolMode = pgbouncer.PoolMode(T.PoolerMode)
 	pgb.PgBouncer.ListenPort = T.PoolerPort
 	pgb.PgBouncer.ListenAddr = "*"
 	pgb.PgBouncer.AuthType = "md5"
 	pgb.PgBouncer.AuthFile = pgbouncer.AuthFile{
-		T.PGUser: T.PGPassword,
+		T.User: T.Password,
 	}
-	pgb.PgBouncer.AdminUsers = []string{T.PGUser}
-	pgb.PgBouncer.AuthQuery = fmt.Sprintf("SELECT * FROM %s.user_lookup($1)", T.PGSchema)
+	pgb.PgBouncer.AdminUsers = []string{T.User}
+	pgb.PgBouncer.AuthQuery = fmt.Sprintf("SELECT * FROM %s.user_lookup($1)", T.Schema)
 	pgb.PgBouncer.LogFile = "/var/log/pgbouncer/pgbouncer.log"
 	pgb.PgBouncer.PidFile = "/var/run/pgbouncer/pgbouncer.pid"
 
diff --git a/lib/gat/handlers/zalando/config.go b/lib/gat/handlers/zalando/config.go
deleted file mode 100644
index 5b2db0f4..00000000
--- a/lib/gat/handlers/zalando/config.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package zalando
-
-type Config struct {
-	PGHost              string `json:"pg_host"`
-	PGPort              int    `json:"pg_port"`
-	PGUser              string `json:"pg_user"`
-	PGSchema            string `json:"pg_schema"`
-	PGPassword          string `json:"pg_password"`
-	PoolerPort          int    `json:"pooler_port"`
-	PoolerMode          string `json:"pooler_mode"`
-	PoolerDefaultSize   int    `json:"pooler_default_size"`
-	PoolerMinSize       int    `json:"pooler_min_size"`
-	PoolerReserveSize   int    `json:"pooler_reserve_size"`
-	PoolerMaxClientConn int    `json:"pooler_max_client_conn"`
-	PoolerMaxDBConn     int    `json:"pooler_max_db_conn"`
-}
diff --git a/lib/gat/standard/standard.go b/lib/gat/standard/standard.go
index 66e343f9..fd5bce70 100644
--- a/lib/gat/standard/standard.go
+++ b/lib/gat/standard/standard.go
@@ -25,7 +25,7 @@ import (
 	// handlers
 	_ "gfx.cafe/gfx/pggat/lib/gat/handlers/discovery"
 	_ "gfx.cafe/gfx/pggat/lib/gat/handlers/pgbouncer"
-	_ "gfx.cafe/gfx/pggat/lib/gat/handlers/zalando"
+	_ "gfx.cafe/gfx/pggat/lib/gat/handlers/pgbouncer_spilo"
 
 	// discovery
 	_ "gfx.cafe/gfx/pggat/lib/gat/handlers/discovery/discoverers/digitalocean"
-- 
GitLab