Skip to content

Commit 83cd1c0

Browse files
committed
fix: make yaml optional and always overlay env vars on config
Previously config loading was binary: yaml-only or env-only. Now yaml is optional (silently skipped if missing), env vars always apply on top. --config-from-env and BROTHER_EXPORTER_CONFIG_FROM_ENV deprecated as no-ops with a runtime warning.
1 parent b281c4f commit 83cd1c0

2 files changed

Lines changed: 40 additions & 125 deletions

File tree

cmd/main.go

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,6 @@ import (
1515
promexporter_metrics "github.com/d0ugal/promexporter/metrics"
1616
)
1717

18-
// hasEnvironmentVariables checks if any BROTHER_EXPORTER_* environment variables are set
19-
func hasEnvironmentVariables() bool {
20-
envVars := []string{
21-
"BROTHER_EXPORTER_SERVER_HOST",
22-
"BROTHER_EXPORTER_SERVER_PORT",
23-
"BROTHER_EXPORTER_LOG_LEVEL",
24-
"BROTHER_EXPORTER_LOG_FORMAT",
25-
"BROTHER_EXPORTER_METRICS_DEFAULT_INTERVAL",
26-
"BROTHER_EXPORTER_PRINTER_HOST",
27-
"BROTHER_EXPORTER_PRINTER_COMMUNITY",
28-
"BROTHER_EXPORTER_PRINTER_TYPE",
29-
}
30-
31-
for _, envVar := range envVars {
32-
if os.Getenv(envVar) != "" {
33-
return true
34-
}
35-
}
36-
37-
return false
38-
}
39-
4018
func main() {
4119
// Parse command line flags
4220
var showVersion bool
@@ -49,7 +27,7 @@ func main() {
4927
)
5028

5129
flag.StringVar(&configPath, "config", "config.yaml", "Path to configuration file")
52-
flag.BoolVar(&configFromEnv, "config-from-env", false, "Load configuration from environment variables only")
30+
flag.BoolVar(&configFromEnv, "config-from-env", false, "Deprecated: env vars are always applied; this flag is a no-op")
5331
flag.Parse()
5432

5533
// Show version if requested
@@ -60,26 +38,17 @@ func main() {
6038
os.Exit(0)
6139
}
6240

63-
// Use environment variable if config flag is not provided
64-
if configPath == "config.yaml" && !configFromEnv {
41+
if configPath == "config.yaml" {
6542
if envConfig := os.Getenv("CONFIG_PATH"); envConfig != "" {
6643
configPath = envConfig
6744
}
6845
}
6946

70-
// Check if we should use environment-only configuration
71-
if !configFromEnv {
72-
// Check explicit flag first
73-
if os.Getenv("BROTHER_EXPORTER_CONFIG_FROM_ENV") == "true" {
74-
configFromEnv = true
75-
} else if hasEnvironmentVariables() {
76-
// Auto-detect environment variables and use them
77-
configFromEnv = true
78-
}
47+
if configFromEnv || os.Getenv("BROTHER_EXPORTER_CONFIG_FROM_ENV") == "true" {
48+
fmt.Fprintln(os.Stderr, "Warning: --config-from-env / BROTHER_EXPORTER_CONFIG_FROM_ENV is deprecated and has no effect. Env vars are always applied on top of yaml config.")
7949
}
8050

81-
// Load configuration
82-
cfg, err := config.LoadConfig(configPath, configFromEnv)
51+
cfg, err := config.LoadConfig(configPath)
8352
if err != nil {
8453
slog.Error("Failed to load configuration", "error", err)
8554
os.Exit(1)

internal/config/config.go

Lines changed: 35 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -25,123 +25,69 @@ type PrinterConfig struct {
2525
Interfaces []string `yaml:"interfaces"`
2626
}
2727

28-
// LoadConfig loads configuration from either a YAML file or environment variables
29-
func LoadConfig(path string, configFromEnv bool) (*Config, error) {
30-
if configFromEnv {
31-
return loadFromEnv()
32-
}
33-
34-
return Load(path)
35-
}
36-
37-
// Load loads configuration from a YAML file
38-
func Load(path string) (*Config, error) {
39-
data, err := os.ReadFile(path)
40-
if err != nil {
41-
return nil, fmt.Errorf("failed to read config file: %w", err)
28+
// LoadConfig loads configuration with priority: env vars > yaml file > defaults.
29+
// The yaml file is optional; if path is empty or the file does not exist it is
30+
// silently skipped. Environment variables are always applied on top.
31+
func LoadConfig(path string) (*Config, error) {
32+
var cfg Config
33+
34+
if path != "" {
35+
data, err := os.ReadFile(path)
36+
if err == nil {
37+
if err := yaml.Unmarshal(data, &cfg); err != nil {
38+
return nil, fmt.Errorf("failed to parse config file %s: %w", path, err)
39+
}
40+
} else if !os.IsNotExist(err) {
41+
return nil, fmt.Errorf("failed to read config file %s: %w", path, err)
42+
}
4243
}
4344

44-
var config Config
45-
if err := yaml.Unmarshal(data, &config); err != nil {
46-
return nil, fmt.Errorf("failed to parse config file: %w", err)
45+
if err := promexporter_config.ApplyGenericEnvVars(&cfg.BaseConfig); err != nil {
46+
return nil, fmt.Errorf("failed to apply generic environment variables: %w", err)
4747
}
4848

49-
// Set defaults
50-
setDefaults(&config)
49+
applyEnvVars(&cfg)
50+
setDefaults(&cfg)
5151

52-
// Validate configuration
53-
if err := config.Validate(); err != nil {
52+
if err := cfg.Validate(); err != nil {
5453
return nil, fmt.Errorf("configuration validation failed: %w", err)
5554
}
5655

57-
return &config, nil
56+
return &cfg, nil
5857
}
5958

60-
// loadFromEnv loads configuration from environment variables
61-
func loadFromEnv() (*Config, error) {
62-
config := &Config{}
63-
64-
// Load base configuration from environment
65-
baseConfig := &promexporter_config.BaseConfig{}
66-
67-
// Server configuration
59+
// applyEnvVars overlays Brother-exporter environment variables onto cfg.
60+
// Only variables that are set (non-empty) are applied.
61+
func applyEnvVars(cfg *Config) {
6862
if host := os.Getenv("BROTHER_EXPORTER_SERVER_HOST"); host != "" {
69-
baseConfig.Server.Host = host
70-
} else {
71-
baseConfig.Server.Host = "0.0.0.0"
63+
cfg.Server.Host = host
7264
}
73-
7465
if portStr := os.Getenv("BROTHER_EXPORTER_SERVER_PORT"); portStr != "" {
75-
if port, err := parseInt(portStr); err != nil {
76-
return nil, fmt.Errorf("invalid server port: %w", err)
77-
} else {
78-
baseConfig.Server.Port = port
66+
if port, err := parseInt(portStr); err == nil {
67+
cfg.Server.Port = port
7968
}
80-
} else {
81-
baseConfig.Server.Port = 8080
8269
}
83-
84-
// Logging configuration
8570
if level := os.Getenv("BROTHER_EXPORTER_LOG_LEVEL"); level != "" {
86-
baseConfig.Logging.Level = level
87-
} else {
88-
baseConfig.Logging.Level = "info"
71+
cfg.Logging.Level = level
8972
}
90-
9173
if format := os.Getenv("BROTHER_EXPORTER_LOG_FORMAT"); format != "" {
92-
baseConfig.Logging.Format = format
93-
} else {
94-
baseConfig.Logging.Format = "json"
74+
cfg.Logging.Format = format
9575
}
96-
97-
// Metrics configuration
9876
if intervalStr := os.Getenv("BROTHER_EXPORTER_METRICS_DEFAULT_INTERVAL"); intervalStr != "" {
99-
if interval, err := time.ParseDuration(intervalStr); err != nil {
100-
return nil, fmt.Errorf("invalid metrics default interval: %w", err)
101-
} else {
102-
baseConfig.Metrics.Collection.DefaultInterval = promexporter_config.Duration{Duration: interval}
103-
baseConfig.Metrics.Collection.DefaultIntervalSet = true
77+
if interval, err := time.ParseDuration(intervalStr); err == nil {
78+
cfg.Metrics.Collection.DefaultInterval = promexporter_config.Duration{Duration: interval}
79+
cfg.Metrics.Collection.DefaultIntervalSet = true
10480
}
105-
} else {
106-
baseConfig.Metrics.Collection.DefaultInterval = promexporter_config.Duration{Duration: time.Second * 30}
107-
}
108-
109-
config.BaseConfig = *baseConfig
110-
111-
// Apply generic environment variables (TRACING_ENABLED, PROFILING_ENABLED, etc.)
112-
// These are handled by promexporter and are shared across all exporters
113-
if err := promexporter_config.ApplyGenericEnvVars(&config.BaseConfig); err != nil {
114-
return nil, fmt.Errorf("failed to apply generic environment variables: %w", err)
11581
}
116-
117-
// Printer configuration
11882
if host := os.Getenv("BROTHER_EXPORTER_PRINTER_HOST"); host != "" {
119-
config.Printer.Host = host
120-
} else {
121-
config.Printer.Host = "192.168.1.100"
83+
cfg.Printer.Host = host
12284
}
123-
12485
if community := os.Getenv("BROTHER_EXPORTER_PRINTER_COMMUNITY"); community != "" {
125-
config.Printer.Community = community
126-
} else {
127-
config.Printer.Community = "public"
86+
cfg.Printer.Community = community
12887
}
129-
13088
if printerType := os.Getenv("BROTHER_EXPORTER_PRINTER_TYPE"); printerType != "" {
131-
config.Printer.Type = printerType
132-
} else {
133-
config.Printer.Type = "laser"
89+
cfg.Printer.Type = printerType
13490
}
135-
136-
// Set defaults for any missing values
137-
setDefaults(config)
138-
139-
// Validate configuration
140-
if err := config.Validate(); err != nil {
141-
return nil, fmt.Errorf("configuration validation failed: %w", err)
142-
}
143-
144-
return config, nil
14591
}
14692

14793
// parseInt parses a string to int

0 commit comments

Comments
 (0)