diff --git a/README.md b/README.md index d3f0a58a..55e59a63 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,11 @@ by hand. If you need to change it, see The default `snmp.yml` file covers a variety of common hardware walking them using SNMP v2 GETBULK. +The `--config.file` parameter can be used multiple times to load more than one file. +It also supports [glob filename matching](https://pkg.go.dev/path/filepath#Glob), e.g. `snmp*.yml`. + +Duplicate `module` or `auth` entries are treated as invalid and can not be loaded. + ## Prometheus Configuration The URL params `target`, `auth`, and `module` can be controlled through relabelling. diff --git a/config/config.go b/config/config.go index 3f3ce5ae..428f6882 100644 --- a/config/config.go +++ b/config/config.go @@ -16,6 +16,7 @@ package config import ( "fmt" "os" + "path/filepath" "regexp" "time" @@ -23,15 +24,23 @@ import ( "gopkg.in/yaml.v2" ) -func LoadFile(filename string) (*Config, error) { - content, err := os.ReadFile(filename) - if err != nil { - return nil, err - } +func LoadFile(paths []string) (*Config, error) { cfg := &Config{} - err = yaml.UnmarshalStrict(content, cfg) - if err != nil { - return nil, err + for _, p := range paths { + files, err := filepath.Glob(p) + if err != nil { + return nil, err + } + for _, f := range files { + content, err := os.ReadFile(f) + if err != nil { + return nil, err + } + err = yaml.UnmarshalStrict(content, cfg) + if err != nil { + return nil, err + } + } } return cfg, nil } diff --git a/config_test.go b/config_test.go index dba55fea..31ae12f5 100644 --- a/config_test.go +++ b/config_test.go @@ -22,7 +22,7 @@ import ( func TestHideConfigSecrets(t *testing.T) { sc := &SafeConfig{} - err := sc.ReloadConfig("testdata/snmp-auth.yml") + err := sc.ReloadConfig([]string{"testdata/snmp-auth.yml"}) if err != nil { t.Errorf("Error loading config %v: %v", "testdata/snmp-auth.yml", err) } @@ -41,7 +41,7 @@ func TestHideConfigSecrets(t *testing.T) { func TestLoadConfigWithOverrides(t *testing.T) { sc := &SafeConfig{} - err := sc.ReloadConfig("testdata/snmp-with-overrides.yml") + err := sc.ReloadConfig([]string{"testdata/snmp-with-overrides.yml"}) if err != nil { t.Errorf("Error loading config %v: %v", "testdata/snmp-with-overrides.yml", err) } @@ -52,3 +52,18 @@ func TestLoadConfigWithOverrides(t *testing.T) { t.Errorf("Error marshaling config: %v", err) } } + +func TestLoadMultipleConfigs(t *testing.T) { + sc := &SafeConfig{} + configs := []string{"testdata/snmp-auth.yml", "testdata/snmp-with-overrides.yml"} + err := sc.ReloadConfig(configs) + if err != nil { + t.Errorf("Error loading configs %v: %v", configs, err) + } + sc.RLock() + _, err = yaml.Marshal(sc.C) + sc.RUnlock() + if err != nil { + t.Errorf("Error marshaling config: %v", err) + } +} diff --git a/main.go b/main.go index f89a402d..ed553b09 100644 --- a/main.go +++ b/main.go @@ -41,7 +41,7 @@ import ( ) var ( - configFile = kingpin.Flag("config.file", "Path to configuration file.").Default("snmp.yml").String() + configFile = kingpin.Flag("config.file", "Path to configuration file.").Default("snmp.yml").Strings() dryRun = kingpin.Flag("dry-run", "Only verify configuration is valid and exit.").Default("false").Bool() concurrency = kingpin.Flag("snmp.module-concurrency", "The number of modules to fetch concurrently per scrape").Default("1").Int() metricsPath = kingpin.Flag( @@ -152,7 +152,7 @@ type SafeConfig struct { C *config.Config } -func (sc *SafeConfig) ReloadConfig(configFile string) (err error) { +func (sc *SafeConfig) ReloadConfig(configFile []string) (err error) { conf, err := config.LoadFile(configFile) if err != nil { return err