@@ -7,17 +7,23 @@ import (
77 "path/filepath"
88 "strings"
99
10+ "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
11+
1012 "github.com/aws/aws-sdk-go-v2/service/cloudwatch"
1113 "github.com/aws/aws-sdk-go-v2/service/ec2"
1214 "github.com/aws/aws-sdk-go-v2/service/rds"
1315 "github.com/aws/aws-sdk-go-v2/service/servicequotas"
16+ "github.com/knadh/koanf/parsers/yaml"
17+ "github.com/knadh/koanf/providers/env"
18+ "github.com/knadh/koanf/providers/file"
19+ "github.com/knadh/koanf/providers/posflag"
20+ "github.com/knadh/koanf/v2"
1421 "github.com/prometheus/client_golang/prometheus"
1522 "github.com/qonto/prometheus-rds-exporter/internal/app/exporter"
1623 "github.com/qonto/prometheus-rds-exporter/internal/infra/build"
1724 "github.com/qonto/prometheus-rds-exporter/internal/infra/http"
1825 "github.com/qonto/prometheus-rds-exporter/internal/infra/logger"
1926 "github.com/spf13/cobra"
20- "github.com/spf13/viper"
2127)
2228
2329const (
@@ -27,25 +33,29 @@ const (
2733 awsErrorExitCode = 4
2834)
2935
30- var cfgFile string
36+ var (
37+ cfgFile string
38+ k = koanf .New ("." )
39+ )
3140
3241type exporterConfig struct {
33- Debug bool `mapstructure:"debug"`
34- LogFormat string `mapstructure:"log-format"`
35- TLSCertPath string `mapstructure:"tls-cert-path"`
36- TLSKeyPath string `mapstructure:"tls-key-path"`
37- MetricPath string `mapstructure:"metrics-path"`
38- ListenAddress string `mapstructure:"listen-address"`
39- AWSAssumeRoleSession string `mapstructure:"aws-assume-role-session"`
40- AWSAssumeRoleArn string `mapstructure:"aws-assume-role-arn"`
41- CollectInstanceMetrics bool `mapstructure:"collect-instance-metrics"`
42- CollectInstanceTags bool `mapstructure:"collect-instance-tags"`
43- CollectInstanceTypes bool `mapstructure:"collect-instance-types"`
44- CollectLogsSize bool `mapstructure:"collect-logs-size"`
45- CollectMaintenances bool `mapstructure:"collect-maintenances"`
46- CollectQuotas bool `mapstructure:"collect-quotas"`
47- CollectUsages bool `mapstructure:"collect-usages"`
48- OTELTracesEnabled bool `mapstructure:"enable-otel-traces"`
42+ Debug bool `koanf:"debug"`
43+ LogFormat string `koanf:"log-format"`
44+ TLSCertPath string `koanf:"tls-cert-path"`
45+ TLSKeyPath string `koanf:"tls-key-path"`
46+ MetricPath string `koanf:"metrics-path"`
47+ ListenAddress string `koanf:"listen-address"`
48+ AWSAssumeRoleSession string `koanf:"aws-assume-role-session"`
49+ AWSAssumeRoleArn string `koanf:"aws-assume-role-arn"`
50+ CollectInstanceMetrics bool `koanf:"collect-instance-metrics"`
51+ CollectInstanceTags bool `koanf:"collect-instance-tags"`
52+ CollectInstanceTypes bool `koanf:"collect-instance-types"`
53+ CollectLogsSize bool `koanf:"collect-logs-size"`
54+ CollectMaintenances bool `koanf:"collect-maintenances"`
55+ CollectQuotas bool `koanf:"collect-quotas"`
56+ CollectUsages bool `koanf:"collect-usages"`
57+ OTELTracesEnabled bool `koanf:"enable-otel-traces"`
58+ TagSelections map [string ][]string `koanf:"tag-selections"`
4959}
5060
5161func run (configuration exporterConfig ) {
@@ -55,6 +65,8 @@ func run(configuration exporterConfig) {
5565 panic (err )
5666 }
5767
68+ logger .Debug (fmt .Sprintf ("Config: %+v\n " , configuration ))
69+
5870 cfg , err := getAWSConfiguration (logger , configuration .AWSAssumeRoleArn , configuration .AWSAssumeRoleSession )
5971 if err != nil {
6072 logger .Error ("can't initialize AWS configuration" , "reason" , err )
@@ -68,6 +80,13 @@ func run(configuration exporterConfig) {
6880 }
6981
7082 rdsClient := rds .NewFromConfig (cfg )
83+
84+ var tagClient * resourcegroupstaggingapi.Client
85+
86+ if configuration .TagSelections != nil {
87+ tagClient = resourcegroupstaggingapi .NewFromConfig (cfg )
88+ }
89+
7190 ec2Client := ec2 .NewFromConfig (cfg )
7291 cloudWatchClient := cloudwatch .NewFromConfig (cfg )
7392 servicequotasClient := servicequotas .NewFromConfig (cfg )
@@ -80,9 +99,10 @@ func run(configuration exporterConfig) {
8099 CollectMaintenances : configuration .CollectMaintenances ,
81100 CollectQuotas : configuration .CollectQuotas ,
82101 CollectUsages : configuration .CollectUsages ,
102+ TagSelections : configuration .TagSelections ,
83103 }
84104
85- collector := exporter .NewCollector (* logger , collectorConfiguration , awsAccountID , awsRegion , rdsClient , ec2Client , cloudWatchClient , servicequotasClient )
105+ collector := exporter .NewCollector (* logger , collectorConfiguration , awsAccountID , awsRegion , rdsClient , ec2Client , cloudWatchClient , servicequotasClient , tagClient )
86106
87107 prometheus .MustRegister (collector )
88108
@@ -111,10 +131,16 @@ func NewRootCommand() (*cobra.Command, error) {
111131 Long : `Collect AWS RDS key metrics from AWS APIs
112132 and expose them as Prometheus metrics.` ,
113133 Run : func (cmd * cobra.Command , args []string ) {
114- var c exporterConfig
115- err := viper .Unmarshal (& c )
134+ err := k .Load (posflag .Provider (cmd .Flags (), "." , k ), nil )
116135 if err != nil {
117- fmt .Println ("ERROR: Unable to decode configuration, %w" , err )
136+ fmt .Printf ("ERROR: Unable to interpret flags, %v\n " , err )
137+
138+ return
139+ }
140+
141+ var c exporterConfig
142+ if err := k .Unmarshal ("" , & c ); err != nil {
143+ fmt .Printf ("ERROR: Unable to decode configuration, %v\n " , err )
118144
119145 return
120146 }
@@ -142,86 +168,6 @@ func NewRootCommand() (*cobra.Command, error) {
142168 cmd .Flags ().BoolP ("collect-quotas" , "" , true , "Collect AWS RDS quotas" )
143169 cmd .Flags ().BoolP ("collect-usages" , "" , true , "Collect AWS RDS usages" )
144170
145- err := viper .BindPFlag ("debug" , cmd .Flags ().Lookup ("debug" ))
146- if err != nil {
147- return cmd , fmt .Errorf ("failed to bind 'debug' parameter: %w" , err )
148- }
149-
150- err = viper .BindPFlag ("log-format" , cmd .Flags ().Lookup ("log-format" ))
151- if err != nil {
152- return cmd , fmt .Errorf ("failed to bind 'log-format' parameter: %w" , err )
153- }
154-
155- err = viper .BindPFlag ("enable-otel-traces" , cmd .Flags ().Lookup ("enable-otel-traces" ))
156- if err != nil {
157- return cmd , fmt .Errorf ("failed to bind 'enable-otel-traces' parameter: %w" , err )
158- }
159-
160- err = viper .BindPFlag ("metrics-path" , cmd .Flags ().Lookup ("metrics-path" ))
161- if err != nil {
162- return cmd , fmt .Errorf ("failed to bind 'metrics-path' parameter: %w" , err )
163- }
164-
165- err = viper .BindPFlag ("tls-cert-path" , cmd .Flags ().Lookup ("tls-cert-path" ))
166- if err != nil {
167- return cmd , fmt .Errorf ("failed to bind 'tls-cert-path' parameter: %w" , err )
168- }
169-
170- err = viper .BindPFlag ("tls-key-path" , cmd .Flags ().Lookup ("tls-key-path" ))
171- if err != nil {
172- return cmd , fmt .Errorf ("failed to bind 'tls-key-path' parameter: %w" , err )
173- }
174-
175- err = viper .BindPFlag ("listen-address" , cmd .Flags ().Lookup ("listen-address" ))
176- if err != nil {
177- return cmd , fmt .Errorf ("failed to bind 'listen-address' parameter: %w" , err )
178- }
179-
180- err = viper .BindPFlag ("aws-assume-role-arn" , cmd .Flags ().Lookup ("aws-assume-role-arn" ))
181- if err != nil {
182- return cmd , fmt .Errorf ("failed to bind 'aws-assume-role-arn' parameter: %w" , err )
183- }
184-
185- err = viper .BindPFlag ("aws-assume-role-session" , cmd .Flags ().Lookup ("aws-assume-role-session" ))
186- if err != nil {
187- return cmd , fmt .Errorf ("failed to bind 'aws-assume-role-session' parameter: %w" , err )
188- }
189-
190- err = viper .BindPFlag ("collect-instance-metrics" , cmd .Flags ().Lookup ("collect-instance-metrics" ))
191- if err != nil {
192- return cmd , fmt .Errorf ("failed to bind 'collect-instance-metrics' parameter: %w" , err )
193- }
194-
195- err = viper .BindPFlag ("collect-instance-tags" , cmd .Flags ().Lookup ("collect-instance-tags" ))
196- if err != nil {
197- return cmd , fmt .Errorf ("failed to bind 'collect-instance-tags' parameter: %w" , err )
198- }
199-
200- err = viper .BindPFlag ("collect-instance-types" , cmd .Flags ().Lookup ("collect-instance-types" ))
201- if err != nil {
202- return cmd , fmt .Errorf ("failed to bind 'collect-instance-types' parameter: %w" , err )
203- }
204-
205- err = viper .BindPFlag ("collect-quotas" , cmd .Flags ().Lookup ("collect-quotas" ))
206- if err != nil {
207- return cmd , fmt .Errorf ("failed to bind 'collect-quotas' parameter: %w" , err )
208- }
209-
210- err = viper .BindPFlag ("collect-usages" , cmd .Flags ().Lookup ("collect-usages" ))
211- if err != nil {
212- return cmd , fmt .Errorf ("failed to bind 'collect-usages' parameter: %w" , err )
213- }
214-
215- err = viper .BindPFlag ("collect-logs-size" , cmd .Flags ().Lookup ("collect-logs-size" ))
216- if err != nil {
217- return cmd , fmt .Errorf ("failed to bind 'collect-logs-size' parameter: %w" , err )
218- }
219-
220- err = viper .BindPFlag ("collect-maintenances" , cmd .Flags ().Lookup ("collect-maintenances" ))
221- if err != nil {
222- return cmd , fmt .Errorf ("failed to bind 'collect-maintenances' parameter: %w" , err )
223- }
224-
225171 return cmd , nil
226172}
227173
@@ -243,32 +189,30 @@ func Execute() {
243189func initConfig () {
244190 if cfgFile != "" {
245191 // Use config file from the flag.
246- viper .SetConfigFile (cfgFile )
192+ err := k .Load (file .Provider (cfgFile ), yaml .Parser ())
193+ cobra .CheckErr (err )
247194 } else {
248- // Find home directory
195+ // Find home directory.
249196 home , err := os .UserHomeDir ()
250197 cobra .CheckErr (err )
251198
252- // Search config in home directory or current directory with name "prometheus-rds-exporter.yaml"
253-
199+ // Search config in home directory or current directory with name "prometheus-rds-exporter.yaml".
254200 configurationFilename := "prometheus-rds-exporter.yaml"
255201 currentPathFilename := configurationFilename
256202 homeFilename := filepath .Join (home , configurationFilename )
257203
258- if _ , err := os . Stat ( homeFilename ); err == nil {
259- viper . SetConfigFile ( homeFilename )
204+ if err := k . Load ( file . Provider ( homeFilename ), yaml . Parser () ); err == nil {
205+ fmt . Printf ( "Using config file: %s \n " , homeFilename )
260206 }
261207
262- if _ , err := os . Stat ( currentPathFilename ); err == nil {
263- viper . SetConfigFile ( currentPathFilename )
208+ if err := k . Load ( file . Provider ( currentPathFilename ), yaml . Parser () ); err == nil {
209+ fmt . Printf ( "Using config file: %s \n " , currentPathFilename )
264210 }
265211 }
266212
267- if err := viper .ReadInConfig (); err == nil {
268- fmt .Fprintln (os .Stderr , "Using config file:" , viper .ConfigFileUsed ())
269- }
270-
271- viper .SetEnvPrefix ("prometheus_rds_exporter" ) // will be uppercased automatically
272- viper .SetEnvKeyReplacer (strings .NewReplacer ("-" , "_" ))
273- viper .AutomaticEnv ()
213+ // Set environment variables.
214+ err := k .Load (env .Provider ("PROMETHEUS_RDS_EXPORTER_" , "." , func (s string ) string {
215+ return strings .ReplaceAll (strings .ToLower (strings .TrimPrefix (s , "PROMETHEUS_RDS_EXPORTER_" )), "_" , "." )
216+ }), nil )
217+ cobra .CheckErr (err )
274218}
0 commit comments