Skip to content

Warn if configuration file not found, fail if missing auths or modules #1457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package config
import (
"errors"
"fmt"
"log/slog"
"os"
"path/filepath"
"regexp"
Expand All @@ -25,13 +26,16 @@ import (
"gopkg.in/yaml.v2"
)

func LoadFile(paths []string, expandEnvVars bool) (*Config, error) {
func LoadFile(logger *slog.Logger, paths []string, expandEnvVars bool) (*Config, error) {
cfg := &Config{}
for _, p := range paths {
files, err := filepath.Glob(p)
if err != nil {
return nil, err
}
if len(files) == 0 {
logger.Warn("No file found matching pattern", "file", p)
}
for _, f := range files {
content, err := os.ReadFile(f)
if err != nil {
Expand Down
15 changes: 9 additions & 6 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ import (
"strings"
"testing"

"github.com/prometheus/common/promslog"
yaml "gopkg.in/yaml.v2"
)

var nopLogger = promslog.NewNopLogger()

func TestHideConfigSecrets(t *testing.T) {
sc := &SafeConfig{}
err := sc.ReloadConfig([]string{"testdata/snmp-auth.yml"}, false)
err := sc.ReloadConfig(nopLogger, []string{"testdata/snmp-auth.yml"}, false)
if err != nil {
t.Errorf("Error loading config %v: %v", "testdata/snmp-auth.yml", err)
}
Expand All @@ -41,7 +44,7 @@ func TestHideConfigSecrets(t *testing.T) {

func TestLoadConfigWithOverrides(t *testing.T) {
sc := &SafeConfig{}
err := sc.ReloadConfig([]string{"testdata/snmp-with-overrides.yml"}, false)
err := sc.ReloadConfig(nopLogger, []string{"testdata/snmp-with-overrides.yml"}, false)
if err != nil {
t.Errorf("Error loading config %v: %v", "testdata/snmp-with-overrides.yml", err)
}
Expand All @@ -56,7 +59,7 @@ func TestLoadConfigWithOverrides(t *testing.T) {
func TestLoadMultipleConfigs(t *testing.T) {
sc := &SafeConfig{}
configs := []string{"testdata/snmp-auth.yml", "testdata/snmp-with-overrides.yml"}
err := sc.ReloadConfig(configs, false)
err := sc.ReloadConfig(nopLogger, configs, false)
if err != nil {
t.Errorf("Error loading configs %v: %v", configs, err)
}
Expand All @@ -75,7 +78,7 @@ func TestEnvSecrets(t *testing.T) {
t.Setenv("ENV_PRIV_PASSWORD", "snmp_priv_password")

sc := &SafeConfig{}
err := sc.ReloadConfig([]string{"testdata/snmp-auth-envvars.yml"}, true)
err := sc.ReloadConfig(nopLogger, []string{"testdata/snmp-auth-envvars.yml"}, true)
if err != nil {
t.Errorf("Error loading config %v: %v", "testdata/snmp-auth-envvars.yml", err)
}
Expand Down Expand Up @@ -106,7 +109,7 @@ func TestEnvSecretsMissing(t *testing.T) {
t.Setenv("ENV_PRIV_PASSWORD", "snmp_priv_password")

sc := &SafeConfig{}
err := sc.ReloadConfig([]string{"testdata/snmp-auth-envvars.yml"}, true)
err := sc.ReloadConfig(nopLogger, []string{"testdata/snmp-auth-envvars.yml"}, true)
if err != nil {
// we check the error message pattern to determine the error
if strings.Contains(err.Error(), "environment variable not found") {
Expand All @@ -120,7 +123,7 @@ func TestEnvSecretsMissing(t *testing.T) {
// When SNMPv2 was specified without credentials
func TestEnvSecretsNotSpecified(t *testing.T) {
sc := &SafeConfig{}
err := sc.ReloadConfig([]string{"testdata/snmp-auth-v2nocreds.yml"}, true)
err := sc.ReloadConfig(nopLogger, []string{"testdata/snmp-auth-v2nocreds.yml"}, true)
if err != nil {
t.Errorf("Error loading config %v: %v", "testdata/snmp-auth-v2nocreds.yml", err)
}
Expand Down
22 changes: 16 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ type SafeConfig struct {
C *config.Config
}

func (sc *SafeConfig) ReloadConfig(configFile []string, expandEnvVars bool) (err error) {
conf, err := config.LoadFile(configFile, expandEnvVars)
func (sc *SafeConfig) ReloadConfig(logger *slog.Logger, configFile []string, expandEnvVars bool) (err error) {
conf, err := config.LoadFile(logger, configFile, expandEnvVars)
if err != nil {
return err
}
Expand Down Expand Up @@ -214,10 +214,20 @@ func main() {
prometheus.MustRegister(versioncollector.NewCollector("snmp_exporter"))

// Bail early if the config is bad.
err := sc.ReloadConfig(*configFile, *expandEnvVars)
err := sc.ReloadConfig(logger, *configFile, *expandEnvVars)
if err != nil {
logger.Error("Error parsing config file", "err", err)
logger.Error("Possible version missmatch between generator and snmp_exporter. Make sure generator and snmp_exporter are the same version.")
logger.Error("Possible version mismatch between generator and snmp_exporter. Make sure generator and snmp_exporter are the same version.")
logger.Error("See also: https://github.com/prometheus/snmp_exporter/blob/main/auth-split-migration.md")
os.Exit(1)
}
if len(sc.C.Modules) == 0 {
logger.Error("Configuration is missing Modules. Did you provide any configuration file?")
os.Exit(1)
}
if len(sc.C.Auths) == 0 {
logger.Error("Configuration is missing Auths.")
logger.Error("Possible version mismatch between generator and snmp_exporter. Make sure generator and snmp_exporter are the same version.")
logger.Error("See also: https://github.com/prometheus/snmp_exporter/blob/main/auth-split-migration.md")
os.Exit(1)
}
Expand All @@ -235,13 +245,13 @@ func main() {
for {
select {
case <-hup:
if err := sc.ReloadConfig(*configFile, *expandEnvVars); err != nil {
if err := sc.ReloadConfig(logger, *configFile, *expandEnvVars); err != nil {
logger.Error("Error reloading config", "err", err)
} else {
logger.Info("Loaded config file")
}
case rc := <-reloadCh:
if err := sc.ReloadConfig(*configFile, *expandEnvVars); err != nil {
if err := sc.ReloadConfig(logger, *configFile, *expandEnvVars); err != nil {
logger.Error("Error reloading config", "err", err)
rc <- err
} else {
Expand Down