feat: optional MySQL/MariaDB database driver for deployments that cannot use local SQLite
This commit is contained in:
1 parent
81e299f09a
commit
d752a61e52
35 files changed
+2896
-76
No files matched your search
@@ -0,0 +1,122 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DatabaseDriverSQLite = "sqlite"
|
||||
DatabaseDriverMySQL = "mysql"
|
||||
|
||||
DefaultDatabaseDriver = DatabaseDriverSQLite
|
||||
DefaultDatabasePort = "3306"
|
||||
DefaultDatabaseTLS = "false"
|
||||
DefaultMaxOpenConns = "10"
|
||||
DefaultMaxIdleConns = "5"
|
||||
DefaultConnMaxLifetimeSeconds = "300"
|
||||
EnvDatabaseDriver = "DB_DRIVER"
|
||||
envDatabaseHost = "DB_HOST"
|
||||
envDatabasePort = "DB_PORT"
|
||||
envDatabaseUser = "DB_USER"
|
||||
envDatabasePassword = "DB_PASSWORD"
|
||||
envDatabaseName = "DB_NAME"
|
||||
envDatabaseTLS = "DB_TLS"
|
||||
envDatabaseMaxOpenConns = "DB_MAX_OPEN_CONNS"
|
||||
envDatabaseMaxIdleConns = "DB_MAX_IDLE_CONNS"
|
||||
envDatabaseConnMaxLifetimeSecs = "DB_CONN_MAX_LIFETIME_SECONDS"
|
||||
)
|
||||
|
||||
var mysqlRequiredEnvVars = []string{envDatabaseHost, envDatabaseUser, envDatabasePassword, envDatabaseName}
|
||||
|
||||
var DatabaseTLSModes = []string{"false", "true", "skip-verify", "preferred"}
|
||||
|
||||
type DatabaseSettings struct {
|
||||
Driver string
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Password string
|
||||
Name string
|
||||
TLS string
|
||||
MaxOpenConns int
|
||||
MaxIdleConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
}
|
||||
|
||||
func DatabaseRequiredEnvVars(driver string) []string {
|
||||
if driver == DatabaseDriverMySQL {
|
||||
return slices.Clone(mysqlRequiredEnvVars)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MissingEnvVarError(names []string, lookup func(string) (string, bool)) error {
|
||||
for _, name := range names {
|
||||
if _, isSet := lookup(name); !isSet {
|
||||
return fmt.Errorf("Environment variable %s is not set", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func databaseDriverFromEnv() string {
|
||||
return envOrDefault(EnvDatabaseDriver, DefaultDatabaseDriver)
|
||||
}
|
||||
|
||||
func databaseSettingsFromEnv() (DatabaseSettings, error) {
|
||||
driver := databaseDriverFromEnv()
|
||||
if driver != DatabaseDriverMySQL {
|
||||
return DatabaseSettings{Driver: driver}, nil
|
||||
}
|
||||
|
||||
tls := envOrDefault(envDatabaseTLS, DefaultDatabaseTLS)
|
||||
if !slices.Contains(DatabaseTLSModes, tls) {
|
||||
return DatabaseSettings{}, fmt.Errorf("%s must be one of %s, got %q", envDatabaseTLS, strings.Join(DatabaseTLSModes, ", "), tls)
|
||||
}
|
||||
maxOpen, err := nonNegativeIntEnv(envDatabaseMaxOpenConns, DefaultMaxOpenConns)
|
||||
if err != nil {
|
||||
return DatabaseSettings{}, err
|
||||
}
|
||||
maxIdle, err := nonNegativeIntEnv(envDatabaseMaxIdleConns, DefaultMaxIdleConns)
|
||||
if err != nil {
|
||||
return DatabaseSettings{}, err
|
||||
}
|
||||
lifetimeSeconds, err := nonNegativeIntEnv(envDatabaseConnMaxLifetimeSecs, DefaultConnMaxLifetimeSeconds)
|
||||
if err != nil {
|
||||
return DatabaseSettings{}, err
|
||||
}
|
||||
|
||||
return DatabaseSettings{
|
||||
Driver: driver,
|
||||
Host: os.Getenv(envDatabaseHost),
|
||||
Port: envOrDefault(envDatabasePort, DefaultDatabasePort),
|
||||
User: os.Getenv(envDatabaseUser),
|
||||
Password: os.Getenv(envDatabasePassword),
|
||||
Name: os.Getenv(envDatabaseName),
|
||||
TLS: tls,
|
||||
MaxOpenConns: maxOpen,
|
||||
MaxIdleConns: maxIdle,
|
||||
ConnMaxLifetime: time.Duration(lifetimeSeconds) * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func envOrDefault(name string, fallback string) string {
|
||||
if value := os.Getenv(name); value != "" {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func nonNegativeIntEnv(name string, fallback string) (int, error) {
|
||||
raw := envOrDefault(name, fallback)
|
||||
value, err := strconv.Atoi(raw)
|
||||
if err != nil || value < 0 {
|
||||
return 0, fmt.Errorf("%s must be a non-negative whole number, got %q", name, raw)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
Reference in new issue
Block a user