From 769363d1576d577ce1d2e97d58d4b891cb8b9413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Thu, 24 Apr 2025 23:45:21 +0200 Subject: [PATCH] feat: Add config file validation (#2011) --- .golangci.yaml | 1 + internal/collector/dfsr/dfsr.go | 2 +- internal/collector/dhcp/dhcp.go | 2 +- internal/collector/dns/dns.go | 2 +- internal/collector/exchange/exchange.go | 2 +- internal/collector/filetime/filetime.go | 2 +- internal/collector/hyperv/hyperv.go | 2 +- internal/collector/iis/iis.go | 8 +- .../collector/logical_disk/logical_disk.go | 4 +- internal/collector/mscluster/mscluster.go | 2 +- internal/collector/mssql/mssql.go | 2 +- internal/collector/net/net.go | 6 +- .../collector/netframework/netframework.go | 2 +- .../collector/performancecounter/types.go | 5 ++ .../collector/physical_disk/physical_disk.go | 4 +- internal/collector/printer/printer.go | 4 +- internal/collector/process/process.go | 6 +- .../scheduled_task/scheduled_task.go | 4 +- internal/collector/service/service.go | 4 +- internal/collector/smtp/smtp.go | 4 +- internal/collector/tcp/tcp.go | 2 +- internal/collector/textfile/textfile.go | 2 +- internal/collector/time/time.go | 2 +- internal/config/config.go | 87 +++++++++++++------ pkg/collector/config.go | 12 +-- 25 files changed, 107 insertions(+), 66 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 7bd75b9c..58be63c0 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -19,6 +19,7 @@ linters: - maintidx - mnd - paralleltest + - tagliatelle - testpackage - varnamelen - wrapcheck diff --git a/internal/collector/dfsr/dfsr.go b/internal/collector/dfsr/dfsr.go index 39ae6ccc..1c281574 100644 --- a/internal/collector/dfsr/dfsr.go +++ b/internal/collector/dfsr/dfsr.go @@ -34,7 +34,7 @@ import ( const Name = "dfsr" type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"sources-enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/dhcp/dhcp.go b/internal/collector/dhcp/dhcp.go index 49b6d28b..233a9413 100644 --- a/internal/collector/dhcp/dhcp.go +++ b/internal/collector/dhcp/dhcp.go @@ -41,7 +41,7 @@ const ( ) type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/dns/dns.go b/internal/collector/dns/dns.go index 1176cd9a..dbfa8228 100644 --- a/internal/collector/dns/dns.go +++ b/internal/collector/dns/dns.go @@ -38,7 +38,7 @@ const ( ) type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/exchange/exchange.go b/internal/collector/exchange/exchange.go index d0ba851a..e2ee5800 100644 --- a/internal/collector/exchange/exchange.go +++ b/internal/collector/exchange/exchange.go @@ -46,7 +46,7 @@ const ( ) type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/filetime/filetime.go b/internal/collector/filetime/filetime.go index a92b21e3..6e7f798e 100644 --- a/internal/collector/filetime/filetime.go +++ b/internal/collector/filetime/filetime.go @@ -35,7 +35,7 @@ import ( const Name = "filetime" type Config struct { - FilePatterns []string + FilePatterns []string `yaml:"file-patterns"` } //nolint:gochecknoglobals diff --git a/internal/collector/hyperv/hyperv.go b/internal/collector/hyperv/hyperv.go index 6375e233..7ec3611d 100644 --- a/internal/collector/hyperv/hyperv.go +++ b/internal/collector/hyperv/hyperv.go @@ -52,7 +52,7 @@ const ( ) type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/iis/iis.go b/internal/collector/iis/iis.go index ddbf422d..eed5cad3 100644 --- a/internal/collector/iis/iis.go +++ b/internal/collector/iis/iis.go @@ -35,10 +35,10 @@ import ( const Name = "iis" type Config struct { - SiteInclude *regexp.Regexp `yaml:"site_include"` - SiteExclude *regexp.Regexp `yaml:"site_exclude"` - AppInclude *regexp.Regexp `yaml:"app_include"` - AppExclude *regexp.Regexp `yaml:"app_exclude"` + SiteInclude *regexp.Regexp `yaml:"site-include"` + SiteExclude *regexp.Regexp `yaml:"site-exclude"` + AppInclude *regexp.Regexp `yaml:"app-include"` + AppExclude *regexp.Regexp `yaml:"app-exclude"` } //nolint:gochecknoglobals diff --git a/internal/collector/logical_disk/logical_disk.go b/internal/collector/logical_disk/logical_disk.go index 90e9a966..afc17a97 100644 --- a/internal/collector/logical_disk/logical_disk.go +++ b/internal/collector/logical_disk/logical_disk.go @@ -38,8 +38,8 @@ import ( const Name = "logical_disk" type Config struct { - VolumeInclude *regexp.Regexp `yaml:"volume_include"` - VolumeExclude *regexp.Regexp `yaml:"volume_exclude"` + VolumeInclude *regexp.Regexp `yaml:"volume-include"` + VolumeExclude *regexp.Regexp `yaml:"volume-exclude"` } //nolint:gochecknoglobals diff --git a/internal/collector/mscluster/mscluster.go b/internal/collector/mscluster/mscluster.go index 17418cfd..21d2dfad 100644 --- a/internal/collector/mscluster/mscluster.go +++ b/internal/collector/mscluster/mscluster.go @@ -41,7 +41,7 @@ const ( ) type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/mssql/mssql.go b/internal/collector/mssql/mssql.go index 8942c421..6deab7a0 100644 --- a/internal/collector/mssql/mssql.go +++ b/internal/collector/mssql/mssql.go @@ -54,7 +54,7 @@ const ( ) type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/net/net.go b/internal/collector/net/net.go index d8415a28..1b7f0fde 100644 --- a/internal/collector/net/net.go +++ b/internal/collector/net/net.go @@ -43,9 +43,9 @@ const ( ) type Config struct { - NicExclude *regexp.Regexp `yaml:"nic_exclude"` - NicInclude *regexp.Regexp `yaml:"nic_include"` - CollectorsEnabled []string `yaml:"collectors_enabled"` + NicExclude *regexp.Regexp `yaml:"nic-exclude"` + NicInclude *regexp.Regexp `yaml:"nic-include"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/netframework/netframework.go b/internal/collector/netframework/netframework.go index f4de7702..e80cef01 100644 --- a/internal/collector/netframework/netframework.go +++ b/internal/collector/netframework/netframework.go @@ -33,7 +33,7 @@ import ( const Name = "netframework" type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/performancecounter/types.go b/internal/collector/performancecounter/types.go index 6cc4b6f0..6a75bfcc 100644 --- a/internal/collector/performancecounter/types.go +++ b/internal/collector/performancecounter/types.go @@ -19,6 +19,7 @@ package performancecounter import ( "github.com/prometheus-community/windows_exporter/internal/pdh" + "gopkg.in/yaml.v3" ) type Object struct { @@ -41,3 +42,7 @@ type Counter struct { } // https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/54691ebe11bb9ec32b4e35cd31fcb94a352de134/receiver/windowsperfcountersreceiver/README.md?plain=1#L150 + +func (*Config) UnmarshalYAML(*yaml.Node) error { + return nil +} diff --git a/internal/collector/physical_disk/physical_disk.go b/internal/collector/physical_disk/physical_disk.go index 2887e564..8f6bfc79 100644 --- a/internal/collector/physical_disk/physical_disk.go +++ b/internal/collector/physical_disk/physical_disk.go @@ -33,8 +33,8 @@ import ( const Name = "physical_disk" type Config struct { - DiskInclude *regexp.Regexp `yaml:"disk_include"` - DiskExclude *regexp.Regexp `yaml:"disk_exclude"` + DiskInclude *regexp.Regexp `yaml:"disk-include"` + DiskExclude *regexp.Regexp `yaml:"disk-exclude"` } //nolint:gochecknoglobals diff --git a/internal/collector/printer/printer.go b/internal/collector/printer/printer.go index 55b92f6b..d930fbb4 100644 --- a/internal/collector/printer/printer.go +++ b/internal/collector/printer/printer.go @@ -46,8 +46,8 @@ var printerStatusMap = map[uint16]string{ } type Config struct { - PrinterInclude *regexp.Regexp `yaml:"printer_include"` - PrinterExclude *regexp.Regexp `yaml:"printer_exclude"` + PrinterInclude *regexp.Regexp `yaml:"include"` + PrinterExclude *regexp.Regexp `yaml:"exclude"` } //nolint:gochecknoglobals diff --git a/internal/collector/process/process.go b/internal/collector/process/process.go index f32b4873..c7b8f64a 100644 --- a/internal/collector/process/process.go +++ b/internal/collector/process/process.go @@ -39,9 +39,9 @@ import ( const Name = "process" type Config struct { - ProcessInclude *regexp.Regexp `yaml:"process_include"` - ProcessExclude *regexp.Regexp `yaml:"process_exclude"` - EnableWorkerProcess bool `yaml:"enable_iis_worker_process"` //nolint:tagliatelle + ProcessInclude *regexp.Regexp `yaml:"include"` + ProcessExclude *regexp.Regexp `yaml:"exclude"` + EnableWorkerProcess bool `yaml:"iis"` } //nolint:gochecknoglobals diff --git a/internal/collector/scheduled_task/scheduled_task.go b/internal/collector/scheduled_task/scheduled_task.go index fea00d2f..a8044369 100644 --- a/internal/collector/scheduled_task/scheduled_task.go +++ b/internal/collector/scheduled_task/scheduled_task.go @@ -36,8 +36,8 @@ import ( const Name = "scheduled_task" type Config struct { - TaskExclude *regexp.Regexp `yaml:"task_exclude"` - TaskInclude *regexp.Regexp `yaml:"task_include"` + TaskExclude *regexp.Regexp `yaml:"exclude"` + TaskInclude *regexp.Regexp `yaml:"include"` } //nolint:gochecknoglobals diff --git a/internal/collector/service/service.go b/internal/collector/service/service.go index 8972ccfe..a6ae3e9c 100644 --- a/internal/collector/service/service.go +++ b/internal/collector/service/service.go @@ -38,8 +38,8 @@ import ( const Name = "service" type Config struct { - ServiceInclude *regexp.Regexp `yaml:"service_include"` - ServiceExclude *regexp.Regexp `yaml:"service_exclude"` + ServiceInclude *regexp.Regexp `yaml:"include"` + ServiceExclude *regexp.Regexp `yaml:"exclude"` } //nolint:gochecknoglobals diff --git a/internal/collector/smtp/smtp.go b/internal/collector/smtp/smtp.go index 8532980c..451ec2bb 100644 --- a/internal/collector/smtp/smtp.go +++ b/internal/collector/smtp/smtp.go @@ -32,8 +32,8 @@ import ( const Name = "smtp" type Config struct { - ServerInclude *regexp.Regexp `yaml:"server_include"` - ServerExclude *regexp.Regexp `yaml:"server_exclude"` + ServerInclude *regexp.Regexp `yaml:"server-include"` + ServerExclude *regexp.Regexp `yaml:"server-exclude"` } //nolint:gochecknoglobals diff --git a/internal/collector/tcp/tcp.go b/internal/collector/tcp/tcp.go index 173827b1..208d545b 100644 --- a/internal/collector/tcp/tcp.go +++ b/internal/collector/tcp/tcp.go @@ -36,7 +36,7 @@ import ( const Name = "tcp" type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/collector/textfile/textfile.go b/internal/collector/textfile/textfile.go index d3574e5f..0aa227a6 100644 --- a/internal/collector/textfile/textfile.go +++ b/internal/collector/textfile/textfile.go @@ -41,7 +41,7 @@ import ( const Name = "textfile" type Config struct { - TextFileDirectories []string `yaml:"text_file_directories"` + TextFileDirectories []string `yaml:"directories"` } //nolint:gochecknoglobals diff --git a/internal/collector/time/time.go b/internal/collector/time/time.go index f61daa27..dc10884c 100644 --- a/internal/collector/time/time.go +++ b/internal/collector/time/time.go @@ -43,7 +43,7 @@ const ( ) type Config struct { - CollectorsEnabled []string `yaml:"collectors_enabled"` + CollectorsEnabled []string `yaml:"enabled"` } //nolint:gochecknoglobals diff --git a/internal/config/config.go b/internal/config/config.go index dd16ff00..280d06fb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,13 +19,49 @@ package config import ( "fmt" + "io" "os" "strings" "github.com/alecthomas/kingpin/v2" + "github.com/prometheus-community/windows_exporter/pkg/collector" "gopkg.in/yaml.v3" ) +// configFile represents the structure of the windows_exporter configuration file, +// including configuration from the collector and web packages. +type configFile struct { + Debug struct { + Enabled bool `yaml:"enabled"` + } `yaml:"debug"` + Collectors struct { + Enabled string `yaml:"enabled"` + } `yaml:"collectors"` + Collector collector.Config `yaml:"collector"` + Log struct { + Level string `yaml:"level"` + Format string `yaml:"format"` + File string `yaml:"file"` + } `yaml:"log"` + Process struct { + Priority string `yaml:"priority"` + MemoryLimit string `yaml:"memory-limit"` + } `yaml:"process"` + Scrape struct { + TimeoutMargin string `yaml:"timeout-margin"` + } `yaml:"scrape"` + Telemetry struct { + Path string `yaml:"path"` + } `yaml:"telemetry"` + Web struct { + DisableExporterMetrics bool `yaml:"disable-exporter-metrics"` + ListenAddresses any `yaml:"listen-address"` + Config struct { + File string `yaml:"file"` + } `yaml:"config"` + } `yaml:"web"` +} + type getFlagger interface { GetFlag(name string) *kingpin.FlagClause } @@ -80,24 +116,37 @@ func ParseConfigFile(args []string) string { } // NewConfigFileResolver returns a Resolver structure. -func NewConfigFileResolver(file string) (*Resolver, error) { +func NewConfigFileResolver(filePath string) (*Resolver, error) { flags := map[string]string{} - var ( - err error - fileBytes []byte - ) - - fileBytes, err = readFromFile(file) + file, err := os.Open(filePath) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to open configuration file: %w", err) + } + + defer func() { + _ = file.Close() + }() + + var configFileStructure configFile + + decoder := yaml.NewDecoder(file) + decoder.KnownFields(true) + + if err = decoder.Decode(&configFileStructure); err != nil { + return nil, fmt.Errorf("configuration file validation error: %w", err) + } + + _, err = file.Seek(0, io.SeekStart) + if err != nil { + return nil, fmt.Errorf("failed to rewind file: %w", err) } var rawValues map[string]interface{} - err = yaml.Unmarshal(fileBytes, &rawValues) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal configuration file: %w", err) + decoder = yaml.NewDecoder(file) + if err = decoder.Decode(&rawValues); err != nil { + return nil, fmt.Errorf("failed to parse configuration file: %w", err) } // Flatten nested YAML values @@ -111,23 +160,9 @@ func NewConfigFileResolver(file string) (*Resolver, error) { return &Resolver{flags: flags}, nil } -func readFromFile(file string) ([]byte, error) { - if _, err := os.Stat(file); err != nil { - return nil, fmt.Errorf("failed to read configuration file: %w", err) - } - - fileBytes, err := os.ReadFile(file) - if err != nil { - return nil, fmt.Errorf("failed to read configuration file: %w", err) - } - - return fileBytes, nil -} - func (c *Resolver) setDefault(v getFlagger) { for name, value := range c.flags { - f := v.GetFlag(name) - if f != nil { + if f := v.GetFlag(name); f != nil { f.Default(value) } } diff --git a/pkg/collector/config.go b/pkg/collector/config.go index 498632e7..31d5e08e 100644 --- a/pkg/collector/config.go +++ b/pkg/collector/config.go @@ -79,26 +79,26 @@ type Config struct { Cs cs.Config `yaml:"cs"` DFSR dfsr.Config `yaml:"dfsr"` Dhcp dhcp.Config `yaml:"dhcp"` - DiskDrive diskdrive.Config `yaml:"disk_drive"` + DiskDrive diskdrive.Config `yaml:"diskdrive"` DNS dns.Config `yaml:"dns"` Exchange exchange.Config `yaml:"exchange"` Filetime filetime.Config `yaml:"filetime"` Fsrmquota fsrmquota.Config `yaml:"fsrmquota"` - HyperV hyperv.Config `yaml:"hyper_v"` + HyperV hyperv.Config `yaml:"hyperv"` IIS iis.Config `yaml:"iis"` License license.Config `yaml:"license"` LogicalDisk logical_disk.Config `yaml:"logical_disk"` Logon logon.Config `yaml:"logon"` Memory memory.Config `yaml:"memory"` - MSCluster mscluster.Config `yaml:"ms_cluster"` + MSCluster mscluster.Config `yaml:"mscluster"` Msmq msmq.Config `yaml:"msmq"` Mssql mssql.Config `yaml:"mssql"` Net net.Config `yaml:"net"` - NetFramework netframework.Config `yaml:"net_framework"` + NetFramework netframework.Config `yaml:"netframework"` Nps nps.Config `yaml:"nps"` OS os.Config `yaml:"os"` Paging pagefile.Config `yaml:"paging"` - PerformanceCounter performancecounter.Config `yaml:"performance_counter"` + PerformanceCounter performancecounter.Config `yaml:"performancecounter"` PhysicalDisk physical_disk.Config `yaml:"physical_disk"` Printer printer.Config `yaml:"printer"` Process process.Config `yaml:"process"` @@ -112,7 +112,7 @@ type Config struct { TCP tcp.Config `yaml:"tcp"` TerminalServices terminal_services.Config `yaml:"terminal_services"` Textfile textfile.Config `yaml:"textfile"` - ThermalZone thermalzone.Config `yaml:"thermal_zone"` + ThermalZone thermalzone.Config `yaml:"thermalzone"` Time time.Config `yaml:"time"` UDP udp.Config `yaml:"udp"` Update update.Config `yaml:"update"`