From a57e1220839c229b5b5db69f7b8314cb859d79a0 Mon Sep 17 00:00:00 2001
From: Garet Halliday <me@garet.holiday>
Date: Tue, 26 Sep 2023 14:55:59 -0500
Subject: [PATCH] thinking

---
 Caddyfile                                     | 13 +++++
 cmd/caddygat/main.go                          | 11 ++++
 contrib/caddy/Caddyfile                       |  3 --
 contrib/caddy/pggat.go                        | 35 +++++++++++++
 contrib/caddy/pgtype.go                       | 52 +++++++++++++++++++
 contrib/caddy/postgres.go                     | 31 -----------
 contrib/caddy/server.go                       | 21 +-------
 contrib/caddy/slug.go                         | 30 +++++++++++
 go.mod                                        |  5 ++
 go.sum                                        | 10 ++++
 lib/gat/modules/cloud_sql_discovery/config.go |  8 +--
 .../modules/digitalocean_discovery/config.go  |  6 +--
 lib/gat/modules/zalando/config.go             | 24 ++++-----
 .../zalando_operator_discovery/config.go      |  8 +--
 pggat.Dockerfile                              |  2 +-
 15 files changed, 182 insertions(+), 77 deletions(-)
 create mode 100644 Caddyfile
 create mode 100644 cmd/caddygat/main.go
 delete mode 100644 contrib/caddy/Caddyfile
 create mode 100644 contrib/caddy/pggat.go
 create mode 100644 contrib/caddy/pgtype.go
 delete mode 100644 contrib/caddy/postgres.go
 create mode 100644 contrib/caddy/slug.go

diff --git a/Caddyfile b/Caddyfile
new file mode 100644
index 00000000..7e04157c
--- /dev/null
+++ b/Caddyfile
@@ -0,0 +1,13 @@
+:5432, :5431, test@:5430 {
+	digitalocean_databases {
+		"api_key": "dop_v1_061d79431bde02f3ddaddf7aefa118b58ce691dd476bea158d2bbbdb2d7d0bc3",
+		"private": false,
+		"pool_mode": "transaction"
+	}
+}
+
+admin@:6432 {
+	pgbouncer {
+		"foo": "bar"
+	}
+}
diff --git a/cmd/caddygat/main.go b/cmd/caddygat/main.go
new file mode 100644
index 00000000..89d5bbb2
--- /dev/null
+++ b/cmd/caddygat/main.go
@@ -0,0 +1,11 @@
+package main
+
+import (
+	caddycmd "github.com/caddyserver/caddy/v2/cmd"
+
+	_ "gfx.cafe/gfx/pggat/contrib/caddy"
+)
+
+func main() {
+	caddycmd.Main()
+}
diff --git a/contrib/caddy/Caddyfile b/contrib/caddy/Caddyfile
deleted file mode 100644
index 7fbd39e2..00000000
--- a/contrib/caddy/Caddyfile
+++ /dev/null
@@ -1,3 +0,0 @@
-postgres://:5432 {
-
-}
diff --git a/contrib/caddy/pggat.go b/contrib/caddy/pggat.go
new file mode 100644
index 00000000..0aac54d1
--- /dev/null
+++ b/contrib/caddy/pggat.go
@@ -0,0 +1,35 @@
+package caddy
+
+import (
+	"github.com/caddyserver/caddy/v2"
+)
+
+func init() {
+	caddy.RegisterModule((*PGGat)(nil))
+}
+
+type PGGat struct {
+	Servers []Server `json:"servers,omitempty"`
+}
+
+func (*PGGat) CaddyModule() caddy.ModuleInfo {
+	return caddy.ModuleInfo{
+		ID: "pggat",
+		New: func() caddy.Module {
+			return new(PGGat)
+		},
+	}
+}
+
+func (T *PGGat) Start() error {
+	// TODO(garet)
+	return nil
+}
+
+func (T *PGGat) Stop() error {
+	// TODO(garet)
+	return nil
+}
+
+var _ caddy.Module = (*PGGat)(nil)
+var _ caddy.App = (*PGGat)(nil)
diff --git a/contrib/caddy/pgtype.go b/contrib/caddy/pgtype.go
new file mode 100644
index 00000000..0e666a42
--- /dev/null
+++ b/contrib/caddy/pgtype.go
@@ -0,0 +1,52 @@
+package caddy
+
+import (
+	"encoding/json"
+
+	"github.com/caddyserver/caddy/v2"
+	"github.com/caddyserver/caddy/v2/caddyconfig"
+	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+)
+
+func init() {
+	caddyconfig.RegisterAdapter("caddyfile", caddyfile.Adapter{ServerType: ServerType{}})
+}
+
+type ServerType struct{}
+
+func (ServerType) Setup(blocks []caddyfile.ServerBlock, m map[string]any) (*caddy.Config, []caddyconfig.Warning, error) {
+	var config caddy.Config
+	var warnings []caddyconfig.Warning
+
+	var postgres PGGat
+
+	for _, block := range blocks {
+		var server Server
+		// TODO(garet) populate server
+
+		server.Listen = make([]ServerSlug, 0, len(block.Keys))
+		for _, key := range block.Keys {
+			var slug ServerSlug
+			if err := slug.FromString(key); err != nil {
+				return nil, nil, err
+			}
+
+			server.Listen = append(server.Listen, slug)
+		}
+
+		postgres.Servers = append(postgres.Servers, server)
+	}
+
+	if config.AppsRaw == nil {
+		config.AppsRaw = make(caddy.ModuleMap)
+	}
+	raw, err := json.Marshal(postgres)
+	if err != nil {
+		return nil, nil, err
+	}
+	config.AppsRaw["pggat"] = raw
+
+	return &config, warnings, nil
+}
+
+var _ caddyfile.ServerType = ServerType{}
diff --git a/contrib/caddy/postgres.go b/contrib/caddy/postgres.go
deleted file mode 100644
index fac5b3f3..00000000
--- a/contrib/caddy/postgres.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package caddy
-
-import "github.com/caddyserver/caddy/v2"
-
-func init() {
-	caddy.RegisterModule(Postgres{})
-}
-
-type Postgres struct{}
-
-func (T Postgres) CaddyModule() caddy.ModuleInfo {
-	return caddy.ModuleInfo{
-		ID: "postgres",
-		New: func() caddy.Module {
-			return Postgres{}
-		},
-	}
-}
-
-func (T Postgres) Start() error {
-	// TODO(garet)
-	return nil
-}
-
-func (T Postgres) Stop() error {
-	// TODO(garet)
-	return nil
-}
-
-var _ caddy.Module = Postgres{}
-var _ caddy.App = Postgres{}
diff --git a/contrib/caddy/server.go b/contrib/caddy/server.go
index caa8ced7..21c28d0d 100644
--- a/contrib/caddy/server.go
+++ b/contrib/caddy/server.go
@@ -1,22 +1,5 @@
 package caddy
 
-import (
-	"github.com/caddyserver/caddy/v2"
-)
-
-func init() {
-	caddy.RegisterModule(Server{})
+type Server struct {
+	Listen []ServerSlug `json:"listen,omitempty"`
 }
-
-type Server struct{}
-
-func (T Server) CaddyModule() caddy.ModuleInfo {
-	return caddy.ModuleInfo{
-		ID: "postgres.poolers.pggat",
-		New: func() caddy.Module {
-			return Server{}
-		},
-	}
-}
-
-var _ caddy.Module = Server{}
diff --git a/contrib/caddy/slug.go b/contrib/caddy/slug.go
new file mode 100644
index 00000000..7f640ae5
--- /dev/null
+++ b/contrib/caddy/slug.go
@@ -0,0 +1,30 @@
+package caddy
+
+import (
+	"strconv"
+	"strings"
+)
+
+type ServerSlug struct {
+	User     string `json:"user,omitempty"`
+	Password string `json:"password,omitempty"`
+	Host     string `json:"host,omitempty"`
+	Port     int    `json:"port,omitempty"`
+	Database string `json:"database,omitempty"`
+}
+
+func (T *ServerSlug) FromString(str string) error {
+	userPassword, hostPortDatabase, ok := strings.Cut(str, "@")
+	if !ok {
+		hostPortDatabase = userPassword
+		userPassword = ""
+	}
+	T.User, T.Password, ok = strings.Cut(userPassword, ":")
+	var hostPort string
+	hostPort, T.Database, ok = strings.Cut(hostPortDatabase, "/")
+	var port string
+	T.Host, port, ok = strings.Cut(hostPort, ":")
+	var err error
+	T.Port, err = strconv.Atoi(port)
+	return err
+}
diff --git a/go.mod b/go.mod
index 15639cc9..45ee502e 100644
--- a/go.mod
+++ b/go.mod
@@ -18,9 +18,11 @@ require (
 require (
 	cloud.google.com/go/compute v1.20.1 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
+	github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	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
@@ -45,6 +47,7 @@ require (
 	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 	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
@@ -70,7 +73,9 @@ require (
 	github.com/quic-go/qtls-go1-20 v0.3.1 // indirect
 	github.com/quic-go/quic-go v0.37.5 // indirect
 	github.com/rs/zerolog v1.28.0 // indirect
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/spf13/cobra v1.7.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
 	github.com/zeebo/blake3 v0.2.3 // indirect
diff --git a/go.sum b/go.sum
index debf5ec3..43e4bef8 100644
--- a/go.sum
+++ b/go.sum
@@ -47,6 +47,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
+github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
 github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -73,6 +75,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+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=
@@ -212,6 +216,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 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=
@@ -316,11 +322,15 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
 github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
 github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
diff --git a/lib/gat/modules/cloud_sql_discovery/config.go b/lib/gat/modules/cloud_sql_discovery/config.go
index c6f84166..a519ab4a 100644
--- a/lib/gat/modules/cloud_sql_discovery/config.go
+++ b/lib/gat/modules/cloud_sql_discovery/config.go
@@ -7,10 +7,10 @@ import (
 )
 
 type Config struct {
-	Project       string `env:"PGGAT_GC_PROJECT"`
-	IpAddressType string `env:"PGGAT_GC_IP_ADDR_TYPE" default:"PRIMARY"`
-	AuthUser      string `env:"PGGAT_GC_AUTH_USER" default:"pggat"`
-	AuthPassword  string `env:"PGGAT_GC_AUTH_PASSWORD"`
+	Project       string `env:"PGGAT_GC_PROJECT" json:"project"`
+	IpAddressType string `env:"PGGAT_GC_IP_ADDR_TYPE" default:"PRIMARY" json:"ip_address_type"`
+	AuthUser      string `env:"PGGAT_GC_AUTH_USER" default:"pggat" json:"auth_user"`
+	AuthPassword  string `env:"PGGAT_GC_AUTH_PASSWORD" json:"auth_password"`
 }
 
 func Load() (Config, error) {
diff --git a/lib/gat/modules/digitalocean_discovery/config.go b/lib/gat/modules/digitalocean_discovery/config.go
index d228a3ff..603c8756 100644
--- a/lib/gat/modules/digitalocean_discovery/config.go
+++ b/lib/gat/modules/digitalocean_discovery/config.go
@@ -7,9 +7,9 @@ import (
 )
 
 type Config struct {
-	APIKey   string `env:"PGGAT_DO_API_KEY"`
-	Private  bool   `env:"PGGAT_DO_PRIVATE"`
-	PoolMode string `env:"PGGAT_POOL_MODE"`
+	APIKey   string `env:"PGGAT_DO_API_KEY" json:"api_key"`
+	Private  bool   `env:"PGGAT_DO_PRIVATE" json:"private"`
+	PoolMode string `env:"PGGAT_POOL_MODE" json:"pool_mode"`
 }
 
 func Load() (Config, error) {
diff --git a/lib/gat/modules/zalando/config.go b/lib/gat/modules/zalando/config.go
index ad174dee..2ed316aa 100644
--- a/lib/gat/modules/zalando/config.go
+++ b/lib/gat/modules/zalando/config.go
@@ -7,18 +7,18 @@ import (
 )
 
 type Config struct {
-	PGHost              string `env:"PGHOST"`
-	PGPort              int    `env:"PGPORT"`
-	PGUser              string `env:"PGUSER"`
-	PGSchema            string `env:"PGSCHEMA"`
-	PGPassword          string `env:"PGPASSWORD"`
-	PoolerPort          int    `env:"CONNECTION_POOLER_PORT"`
-	PoolerMode          string `env:"CONNECTION_POOLER_MODE"`
-	PoolerDefaultSize   int    `env:"CONNECTION_POOLER_DEFAULT_SIZE"`
-	PoolerMinSize       int    `env:"CONNECTION_POOLER_MIN_SIZE"`
-	PoolerReserveSize   int    `env:"CONNECTION_POOLER_RESERVE_SIZE"`
-	PoolerMaxClientConn int    `env:"CONNECTION_POOLER_MAX_CLIENT_CONN"`
-	PoolerMaxDBConn     int    `env:"CONNECTION_POOLER_MAX_DB_CONN"`
+	PGHost              string `env:"PGHOST" json:"pg_host"`
+	PGPort              int    `env:"PGPORT" json:"pg_port"`
+	PGUser              string `env:"PGUSER" json:"pg_user"`
+	PGSchema            string `env:"PGSCHEMA" json:"pg_schema"`
+	PGPassword          string `env:"PGPASSWORD" json:"pg_password"`
+	PoolerPort          int    `env:"CONNECTION_POOLER_PORT" json:"pooler_port"`
+	PoolerMode          string `env:"CONNECTION_POOLER_MODE" json:"pooler_mode"`
+	PoolerDefaultSize   int    `env:"CONNECTION_POOLER_DEFAULT_SIZE" json:"pooler_default_size"`
+	PoolerMinSize       int    `env:"CONNECTION_POOLER_MIN_SIZE" json:"pooler_min_size"`
+	PoolerReserveSize   int    `env:"CONNECTION_POOLER_RESERVE_SIZE" json:"pooler_reserve_size"`
+	PoolerMaxClientConn int    `env:"CONNECTION_POOLER_MAX_CLIENT_CONN" json:"pooler_max_client_conn"`
+	PoolerMaxDBConn     int    `env:"CONNECTION_POOLER_MAX_DB_CONN" json:"pooler_max_db_conn"`
 }
 
 func Load() (Config, error) {
diff --git a/lib/gat/modules/zalando_operator_discovery/config.go b/lib/gat/modules/zalando_operator_discovery/config.go
index c4a49451..503de784 100644
--- a/lib/gat/modules/zalando_operator_discovery/config.go
+++ b/lib/gat/modules/zalando_operator_discovery/config.go
@@ -6,11 +6,11 @@ import (
 )
 
 type Config struct {
-	Namespace                   string `env:"PGGAT_NAMESPACE" default:"default"`
-	ConfigMapName               string `env:"CONFIG_MAP_NAME"`
-	OperatorConfigurationObject string `env:"POSTGRES_OPERATOR_CONFIGURATION_OBJECT"`
+	Namespace                   string `env:"PGGAT_NAMESPACE" default:"default" json:"namespace"`
+	ConfigMapName               string `env:"CONFIG_MAP_NAME" json:"config_map_name"`
+	OperatorConfigurationObject string `env:"POSTGRES_OPERATOR_CONFIGURATION_OBJECT" json:"operator_configuration_object"`
 
-	Rest *rest.Config
+	Rest *rest.Config `json:"-"`
 }
 
 func Load() (Config, error) {
diff --git a/pggat.Dockerfile b/pggat.Dockerfile
index 57061010..536178a0 100644
--- a/pggat.Dockerfile
+++ b/pggat.Dockerfile
@@ -16,4 +16,4 @@ COPY entrypoint.sh .
 COPY --from=GOBUILDER /src/cgat /usr/bin/pggat
 
 ENTRYPOINT ["/entrypoint.sh"]
-cmd ["pggat"]
+CMD ["pggat"]
-- 
GitLab