diff --git a/cmd/caddygat/pgbouncer.go b/cmd/caddygat/pgbouncer.go index 74b1db5915d188927d58a4e9f7acc27c3f891409..4fe2f957a4c5b74a0040857b730b2c0720245d11 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 0000000000000000000000000000000000000000..306615d9316975b90b1925975c993ecb4d782f44 --- /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 706ee3ef0a02205ea95d65c8de93efd4799b8496..ac588dfd480eff2336b20d7f6cc84ebc1b9b8816 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 91e67319bb9289efbb9802a3f740a5306fbb6e00..ba1b8f2a7d3ec5723452673eda401e89cbac9272 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 0000000000000000000000000000000000000000..81dc370759448d31b433483a8df6f8a92cd0d5ba --- /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 ff739eca1edb977a0f72b4123a1fe0217ae9edad..5a80a19bf9a7a1a8b1d3e48e55123fd6bb79c43a 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 5b2db0f46fde7c627fc7067e41662013d94b0710..0000000000000000000000000000000000000000 --- 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 66e343f9d45294eca904b9d879921d2dae0ea30c..fd5bce701da2fd2a6ce0b16d44590157a14b38cb 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"