Skip to content

Commit

Permalink
fix: switch from viper to koanf
Browse files Browse the repository at this point in the history
mostly to get around spf13/viper#1014

Signed-off-by: Mårten Svantesson <[email protected]>
  • Loading branch information
msvticket committed Dec 11, 2024
1 parent c85406d commit 0f0ac27
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 170 deletions.
164 changes: 48 additions & 116 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/servicequotas"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/posflag"
"github.com/knadh/koanf/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/qonto/prometheus-rds-exporter/internal/app/exporter"
"github.com/qonto/prometheus-rds-exporter/internal/infra/build"
"github.com/qonto/prometheus-rds-exporter/internal/infra/http"
"github.com/qonto/prometheus-rds-exporter/internal/infra/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

const (
Expand All @@ -28,24 +32,25 @@ const (
)

var cfgFile string
var k = koanf.New(".")

type exporterConfig struct {
Debug bool `mapstructure:"debug"`
LogFormat string `mapstructure:"log-format"`
TLSCertPath string `mapstructure:"tls-cert-path"`
TLSKeyPath string `mapstructure:"tls-key-path"`
MetricPath string `mapstructure:"metrics-path"`
ListenAddress string `mapstructure:"listen-address"`
AWSAssumeRoleSession string `mapstructure:"aws-assume-role-session"`
AWSAssumeRoleArn string `mapstructure:"aws-assume-role-arn"`
CollectInstanceMetrics bool `mapstructure:"collect-instance-metrics"`
CollectInstanceTags bool `mapstructure:"collect-instance-tags"`
CollectInstanceTypes bool `mapstructure:"collect-instance-types"`
CollectLogsSize bool `mapstructure:"collect-logs-size"`
CollectMaintenances bool `mapstructure:"collect-maintenances"`
CollectQuotas bool `mapstructure:"collect-quotas"`
CollectUsages bool `mapstructure:"collect-usages"`
OTELTracesEnabled bool `mapstructure:"enable-otel-traces"`
Debug bool `koanf:"debug"`
LogFormat string `koanf:"log-format"`
TLSCertPath string `koanf:"tls-cert-path"`
TLSKeyPath string `koanf:"tls-key-path"`
MetricPath string `koanf:"metrics-path"`
ListenAddress string `koanf:"listen-address"`
AWSAssumeRoleSession string `koanf:"aws-assume-role-session"`
AWSAssumeRoleArn string `koanf:"aws-assume-role-arn"`
CollectInstanceMetrics bool `koanf:"collect-instance-metrics"`
CollectInstanceTags bool `koanf:"collect-instance-tags"`
CollectInstanceTypes bool `koanf:"collect-instance-types"`
CollectLogsSize bool `koanf:"collect-logs-size"`
CollectMaintenances bool `koanf:"collect-maintenances"`
CollectQuotas bool `koanf:"collect-quotas"`
CollectUsages bool `koanf:"collect-usages"`
OTELTracesEnabled bool `koanf:"enable-otel-traces"`
}

func run(configuration exporterConfig) {
Expand All @@ -54,6 +59,7 @@ func run(configuration exporterConfig) {
fmt.Println("ERROR: Fail to initialize logger: %w", err)
panic(err)
}
logger.Debug(fmt.Sprintf("Config: %+v\n", configuration))

cfg, err := getAWSConfiguration(logger, configuration.AWSAssumeRoleArn, configuration.AWSAssumeRoleSession)
if err != nil {
Expand Down Expand Up @@ -111,11 +117,15 @@ func NewRootCommand() (*cobra.Command, error) {
Long: `Collect AWS RDS key metrics from AWS APIs
and expose them as Prometheus metrics.`,
Run: func(cmd *cobra.Command, args []string) {
var c exporterConfig
err := viper.Unmarshal(&c)
err := k.Load(posflag.Provider(cmd.Flags(), ".", k), nil)
if err != nil {
fmt.Println("ERROR: Unable to decode configuration, %w", err)
fmt.Printf("ERROR: Unable to interpret flags, %v\n", err)
return
}

var c exporterConfig
if err := k.Unmarshal("", &c); err != nil {
fmt.Printf("ERROR: Unable to decode configuration, %v\n", err)
return
}
run(c)
Expand All @@ -142,86 +152,6 @@ func NewRootCommand() (*cobra.Command, error) {
cmd.Flags().BoolP("collect-quotas", "", true, "Collect AWS RDS quotas")
cmd.Flags().BoolP("collect-usages", "", true, "Collect AWS RDS usages")

err := viper.BindPFlag("debug", cmd.Flags().Lookup("debug"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'debug' parameter: %w", err)
}

err = viper.BindPFlag("log-format", cmd.Flags().Lookup("log-format"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'log-format' parameter: %w", err)
}

err = viper.BindPFlag("enable-otel-traces", cmd.Flags().Lookup("enable-otel-traces"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'enable-otel-traces' parameter: %w", err)
}

err = viper.BindPFlag("metrics-path", cmd.Flags().Lookup("metrics-path"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'metrics-path' parameter: %w", err)
}

err = viper.BindPFlag("tls-cert-path", cmd.Flags().Lookup("tls-cert-path"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'tls-cert-path' parameter: %w", err)
}

err = viper.BindPFlag("tls-key-path", cmd.Flags().Lookup("tls-key-path"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'tls-key-path' parameter: %w", err)
}

err = viper.BindPFlag("listen-address", cmd.Flags().Lookup("listen-address"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'listen-address' parameter: %w", err)
}

err = viper.BindPFlag("aws-assume-role-arn", cmd.Flags().Lookup("aws-assume-role-arn"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'aws-assume-role-arn' parameter: %w", err)
}

err = viper.BindPFlag("aws-assume-role-session", cmd.Flags().Lookup("aws-assume-role-session"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'aws-assume-role-session' parameter: %w", err)
}

err = viper.BindPFlag("collect-instance-metrics", cmd.Flags().Lookup("collect-instance-metrics"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'collect-instance-metrics' parameter: %w", err)
}

err = viper.BindPFlag("collect-instance-tags", cmd.Flags().Lookup("collect-instance-tags"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'collect-instance-tags' parameter: %w", err)
}

err = viper.BindPFlag("collect-instance-types", cmd.Flags().Lookup("collect-instance-types"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'collect-instance-types' parameter: %w", err)
}

err = viper.BindPFlag("collect-quotas", cmd.Flags().Lookup("collect-quotas"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'collect-quotas' parameter: %w", err)
}

err = viper.BindPFlag("collect-usages", cmd.Flags().Lookup("collect-usages"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'collect-usages' parameter: %w", err)
}

err = viper.BindPFlag("collect-logs-size", cmd.Flags().Lookup("collect-logs-size"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'collect-logs-size' parameter: %w", err)
}

err = viper.BindPFlag("collect-maintenances", cmd.Flags().Lookup("collect-maintenances"))
if err != nil {
return cmd, fmt.Errorf("failed to bind 'collect-maintenances' parameter: %w", err)
}

return cmd, nil
}

Expand All @@ -243,32 +173,34 @@ func Execute() {
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
if err := k.Load(file.Provider(cfgFile), yaml.Parser()); err != nil {
fmt.Printf("error loading config: %v\n", err)
os.Exit(1)
}
} else {
// Find home directory
// Find home directory.
home, err := os.UserHomeDir()
cobra.CheckErr(err)

// Search config in home directory or current directory with name "prometheus-rds-exporter.yaml"
if err != nil {
fmt.Printf("error finding the home directory: %v\n", err)
os.Exit(1)
}

// Search config in home directory or current directory with name "prometheus-rds-exporter.yaml".
configurationFilename := "prometheus-rds-exporter.yaml"
currentPathFilename := configurationFilename
homeFilename := filepath.Join(home, configurationFilename)

if _, err := os.Stat(homeFilename); err == nil {
viper.SetConfigFile(homeFilename)
if err := k.Load(file.Provider(homeFilename), yaml.Parser()); err == nil {
fmt.Printf("Using config file: %s\n", homeFilename)
}

if _, err := os.Stat(currentPathFilename); err == nil {
viper.SetConfigFile(currentPathFilename)
if err := k.Load(file.Provider(currentPathFilename), yaml.Parser()); err == nil {
fmt.Printf("Using config file: %s\n", currentPathFilename)
}
}

if err := viper.ReadInConfig(); err == nil {
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}

viper.SetEnvPrefix("prometheus_rds_exporter") // will be uppercased automatically
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.AutomaticEnv()
// Set environment variables.
k.Load(env.Provider("PROMETHEUS_RDS_EXPORTER_", ".", func(s string) string {
return strings.Replace(strings.ToLower(strings.TrimPrefix(s, "PROMETHEUS_RDS_EXPORTER_")), "_", ".", -1)
}), nil)
}
1 change: 0 additions & 1 deletion docs/add-configuration-parameter.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Steps:

1. Add a new field in `exporterConfig` structure in `cmd/root.go`
1. Add the parameter in flag `cmd.Flags()`
1. Add the parameter in viper `viper.BindPFlag()`

1. Add tests

Expand Down
26 changes: 11 additions & 15 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ require (
github.com/aws/aws-sdk-go-v2/service/rds v1.81.5
github.com/aws/aws-sdk-go-v2/service/servicequotas v1.23.3
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3
github.com/knadh/koanf/parsers/yaml v0.1.0
github.com/knadh/koanf/providers/env v1.0.0
github.com/knadh/koanf/providers/file v1.1.2
github.com/knadh/koanf/providers/posflag v0.1.0
github.com/knadh/koanf/v2 v2.1.2
github.com/prometheus/client_golang v1.19.1
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0
go.opentelemetry.io/otel v1.27.0
Expand All @@ -41,35 +46,26 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
go.opentelemetry.io/otel/metric v1.27.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
google.golang.org/grpc v1.64.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 0f0ac27

Please sign in to comment.