refactor: remove file-based configuration, use env vars only
All checks were successful
check / check (push) Successful in 1m0s
All checks were successful
check / check (push) Successful in 1m0s
Remove the entire pkg/config package (Viper-based YAML config file loader) and simplify internal/config to read all settings directly from environment variables via os.Getenv(). This eliminates the spurious "Failed to load config" log messages that appeared when no config.yaml file was present. - Delete pkg/config/ (YAML loader, resolver, manager, tests) - Delete configs/config.yaml.example - Simplify internal/config helper functions to use os.Getenv() with defaults instead of falling back to pkgconfig - Update tests to set env vars directly instead of creating in-memory YAML config files via afero - Remove afero, cloud.google.com/*, aws-sdk-go dependencies from go.mod - Update README: document env-var-only configuration, remove YAML/Viper references - Keep godotenv/autoload for .env file convenience in local development closes #27
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
||||
"go.uber.org/fx"
|
||||
"sneak.berlin/go/webhooker/internal/globals"
|
||||
"sneak.berlin/go/webhooker/internal/logger"
|
||||
pkgconfig "sneak.berlin/go/webhooker/pkg/config"
|
||||
|
||||
// Populates the environment from a ./.env file automatically for
|
||||
// development configuration. Kept in one place only (here).
|
||||
@@ -56,38 +55,30 @@ func (c *Config) IsProd() bool {
|
||||
return c.Environment == EnvironmentProd
|
||||
}
|
||||
|
||||
// envString returns the env var value if set, otherwise falls back to pkgconfig.
|
||||
func envString(envKey, configKey string) string {
|
||||
if v := os.Getenv(envKey); v != "" {
|
||||
return v
|
||||
}
|
||||
return pkgconfig.GetString(configKey)
|
||||
// envString returns the value of the named environment variable, or
|
||||
// an empty string if not set.
|
||||
func envString(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
|
||||
// envSecretString returns the env var value if set, otherwise falls back to pkgconfig secrets.
|
||||
func envSecretString(envKey, configKey string) string {
|
||||
if v := os.Getenv(envKey); v != "" {
|
||||
return v
|
||||
}
|
||||
return pkgconfig.GetSecretString(configKey)
|
||||
}
|
||||
|
||||
// envBool returns the env var value parsed as bool, otherwise falls back to pkgconfig.
|
||||
func envBool(envKey, configKey string) bool {
|
||||
if v := os.Getenv(envKey); v != "" {
|
||||
// envBool returns the value of the named environment variable parsed as a
|
||||
// boolean. Returns defaultValue if not set.
|
||||
func envBool(key string, defaultValue bool) bool {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return strings.EqualFold(v, "true") || v == "1"
|
||||
}
|
||||
return pkgconfig.GetBool(configKey)
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// envInt returns the env var value parsed as int, otherwise falls back to pkgconfig.
|
||||
func envInt(envKey, configKey string, defaultValue ...int) int {
|
||||
if v := os.Getenv(envKey); v != "" {
|
||||
// envInt returns the value of the named environment variable parsed as an
|
||||
// integer. Returns defaultValue if not set or unparseable.
|
||||
func envInt(key string, defaultValue int) int {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return pkgconfig.GetInt(configKey, defaultValue...)
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// nolint:revive // lc parameter is required by fx even if unused
|
||||
@@ -106,21 +97,18 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
|
||||
EnvironmentDev, EnvironmentProd, environment)
|
||||
}
|
||||
|
||||
// Set the environment in the config package (for fallback resolution)
|
||||
pkgconfig.SetEnvironment(environment)
|
||||
|
||||
// Load configuration values — env vars take precedence over config.yaml
|
||||
// Load configuration values from environment variables
|
||||
s := &Config{
|
||||
DBURL: envString("DBURL", "dburl"),
|
||||
DataDir: envString("DATA_DIR", "dataDir"),
|
||||
Debug: envBool("DEBUG", "debug"),
|
||||
MaintenanceMode: envBool("MAINTENANCE_MODE", "maintenanceMode"),
|
||||
DevelopmentMode: envBool("DEVELOPMENT_MODE", "developmentMode"),
|
||||
DBURL: envString("DBURL"),
|
||||
DataDir: envString("DATA_DIR"),
|
||||
Debug: envBool("DEBUG", false),
|
||||
MaintenanceMode: envBool("MAINTENANCE_MODE", false),
|
||||
DevelopmentMode: envBool("DEVELOPMENT_MODE", false),
|
||||
Environment: environment,
|
||||
MetricsUsername: envString("METRICS_USERNAME", "metricsUsername"),
|
||||
MetricsPassword: envString("METRICS_PASSWORD", "metricsPassword"),
|
||||
Port: envInt("PORT", "port", 8080),
|
||||
SentryDSN: envSecretString("SENTRY_DSN", "sentryDSN"),
|
||||
MetricsUsername: envString("METRICS_USERNAME"),
|
||||
MetricsPassword: envString("METRICS_PASSWORD"),
|
||||
Port: envInt("PORT", 8080),
|
||||
SentryDSN: envString("SENTRY_DSN"),
|
||||
log: log,
|
||||
params: ¶ms,
|
||||
}
|
||||
|
||||
@@ -4,58 +4,14 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/fx/fxtest"
|
||||
"sneak.berlin/go/webhooker/internal/globals"
|
||||
"sneak.berlin/go/webhooker/internal/logger"
|
||||
pkgconfig "sneak.berlin/go/webhooker/pkg/config"
|
||||
)
|
||||
|
||||
// createTestConfig creates a test configuration file in memory
|
||||
func createTestConfig(fs afero.Fs) error {
|
||||
configYAML := `
|
||||
environments:
|
||||
dev:
|
||||
config:
|
||||
port: 8080
|
||||
debug: true
|
||||
maintenanceMode: false
|
||||
developmentMode: true
|
||||
environment: dev
|
||||
dburl: postgres://test:test@localhost:5432/test_dev?sslmode=disable
|
||||
metricsUsername: testuser
|
||||
metricsPassword: testpass
|
||||
secrets:
|
||||
sentryDSN: ""
|
||||
|
||||
prod:
|
||||
config:
|
||||
port: $ENV:PORT
|
||||
debug: $ENV:DEBUG
|
||||
maintenanceMode: $ENV:MAINTENANCE_MODE
|
||||
developmentMode: false
|
||||
environment: prod
|
||||
dburl: $ENV:DBURL
|
||||
metricsUsername: $ENV:METRICS_USERNAME
|
||||
metricsPassword: $ENV:METRICS_PASSWORD
|
||||
secrets:
|
||||
sentryDSN: $ENV:SENTRY_DSN
|
||||
|
||||
configDefaults:
|
||||
port: 8080
|
||||
debug: false
|
||||
maintenanceMode: false
|
||||
developmentMode: false
|
||||
environment: dev
|
||||
metricsUsername: ""
|
||||
metricsPassword: ""
|
||||
`
|
||||
return afero.WriteFile(fs, "config.yaml", []byte(configYAML), 0644)
|
||||
}
|
||||
|
||||
func TestEnvironmentConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -68,6 +24,7 @@ func TestEnvironmentConfig(t *testing.T) {
|
||||
{
|
||||
name: "default is dev",
|
||||
envValue: "",
|
||||
envVars: map[string]string{"DBURL": "file::memory:?cache=shared"},
|
||||
expectError: false,
|
||||
isDev: true,
|
||||
isProd: false,
|
||||
@@ -75,6 +32,7 @@ func TestEnvironmentConfig(t *testing.T) {
|
||||
{
|
||||
name: "explicit dev",
|
||||
envValue: "dev",
|
||||
envVars: map[string]string{"DBURL": "file::memory:?cache=shared"},
|
||||
expectError: false,
|
||||
isDev: true,
|
||||
isProd: false,
|
||||
@@ -92,21 +50,19 @@ func TestEnvironmentConfig(t *testing.T) {
|
||||
{
|
||||
name: "invalid environment",
|
||||
envValue: "staging",
|
||||
envVars: map[string]string{"DBURL": "file::memory:?cache=shared"},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create in-memory filesystem with test config
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, createTestConfig(fs))
|
||||
pkgconfig.SetFs(fs)
|
||||
|
||||
// Set environment variable if specified
|
||||
if tt.envValue != "" {
|
||||
os.Setenv("WEBHOOKER_ENVIRONMENT", tt.envValue)
|
||||
defer os.Unsetenv("WEBHOOKER_ENVIRONMENT")
|
||||
} else {
|
||||
os.Unsetenv("WEBHOOKER_ENVIRONMENT")
|
||||
}
|
||||
|
||||
// Set additional environment variables
|
||||
|
||||
@@ -2,38 +2,19 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"go.uber.org/fx/fxtest"
|
||||
"sneak.berlin/go/webhooker/internal/config"
|
||||
"sneak.berlin/go/webhooker/internal/globals"
|
||||
"sneak.berlin/go/webhooker/internal/logger"
|
||||
pkgconfig "sneak.berlin/go/webhooker/pkg/config"
|
||||
)
|
||||
|
||||
func TestDatabaseConnection(t *testing.T) {
|
||||
// Set up in-memory config so the test does not depend on config.yaml on disk
|
||||
fs := afero.NewMemMapFs()
|
||||
testConfigYAML := `
|
||||
environments:
|
||||
dev:
|
||||
config:
|
||||
port: 8080
|
||||
debug: false
|
||||
maintenanceMode: false
|
||||
developmentMode: true
|
||||
environment: dev
|
||||
dburl: "file::memory:?cache=shared"
|
||||
secrets:
|
||||
sentryDSN: ""
|
||||
configDefaults:
|
||||
port: 8080
|
||||
`
|
||||
if err := afero.WriteFile(fs, "config.yaml", []byte(testConfigYAML), 0644); err != nil {
|
||||
t.Fatalf("Failed to write test config: %v", err)
|
||||
}
|
||||
pkgconfig.SetFs(fs)
|
||||
// Set DBURL env var for config loading
|
||||
os.Setenv("DBURL", "file::memory:?cache=shared")
|
||||
defer os.Unsetenv("DBURL")
|
||||
|
||||
// Set up test dependencies
|
||||
lc := fxtest.NewLifecycle(t)
|
||||
|
||||
@@ -7,32 +7,20 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/fx/fxtest"
|
||||
"sneak.berlin/go/webhooker/internal/config"
|
||||
"sneak.berlin/go/webhooker/internal/globals"
|
||||
"sneak.berlin/go/webhooker/internal/logger"
|
||||
pkgconfig "sneak.berlin/go/webhooker/pkg/config"
|
||||
)
|
||||
|
||||
func setupTestWebhookDBManager(t *testing.T) (*WebhookDBManager, *fxtest.Lifecycle) {
|
||||
t.Helper()
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
testConfigYAML := `
|
||||
environments:
|
||||
dev:
|
||||
config:
|
||||
port: 8080
|
||||
debug: false
|
||||
dburl: "file::memory:?cache=shared"
|
||||
configDefaults:
|
||||
port: 8080
|
||||
`
|
||||
require.NoError(t, afero.WriteFile(fs, "config.yaml", []byte(testConfigYAML), 0644))
|
||||
pkgconfig.SetFs(fs)
|
||||
// Set DBURL env var for config loading
|
||||
os.Setenv("DBURL", "file::memory:?cache=shared")
|
||||
t.Cleanup(func() { os.Unsetenv("DBURL") })
|
||||
|
||||
lc := fxtest.NewLifecycle(t)
|
||||
|
||||
@@ -52,7 +40,6 @@ configDefaults:
|
||||
DBURL: "file::memory:?cache=shared",
|
||||
DataDir: dataDir,
|
||||
}
|
||||
_ = cfg
|
||||
|
||||
mgr, err := NewWebhookDBManager(lc, WebhookDBManagerParams{
|
||||
Config: cfg,
|
||||
|
||||
Reference in New Issue
Block a user