Skip to content

Commit

Permalink
Merge pull request #34 from kpetremann/refacto_configuration
Browse files Browse the repository at this point in the history
Refacto configuration
  • Loading branch information
kpetremann authored Jun 30, 2023
2 parents 203e58d + c29c864 commit 0b9ebc2
Show file tree
Hide file tree
Showing 8 changed files with 704 additions and 73 deletions.
139 changes: 139 additions & 0 deletions cmd/salt-exporter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package main

import (
"errors"
"flag"
"fmt"

"github.com/kpetremann/salt-exporter/internal/metrics"
"github.com/spf13/viper"
)

const defaultLogLevel = "info"
const defaultPort = 2112
const defaultHealthMinion = true
const defaultHealthFunctionsFilter = "state.highstate"
const defaultHealthStatesFilter = "highstate"

var flagConfigMapping = map[string]string{
"host": "listen-address",
"port": "listen-port",
"tls": "tls.enabled",
"tls-cert": "tls.certificate",
"tls-key": "tls.key",
"ignore-test": "metrics.global.filters.ignore-test",
"ignore-mock": "metrics.global.filters.ignore-mock",
"health-minions": "metrics.health-minions",
"health-functions-filter": "metrics.salt_function_status.filters.functions",
"health-states-filter": "metrics.salt_function_status.filters.states",
}

type Config struct {
LogLevel string `mapstructure:"log-level"`

ListenAddress string `mapstructure:"listen-address"`
ListenPort int `mapstructure:"listen-port"`
TLS struct {
Enabled bool
Key string
Certificate string
}

Metrics metrics.Config
}

func parseFlags() {
// flags
flag.String("log-level", defaultLogLevel, "log level (debug, info, warn, error, fatal, panic, disabled)")

flag.String("host", "", "listen address")
flag.Int("port", defaultPort, "listen port")
flag.Bool("tls", false, "enable TLS")
flag.String("tls-cert", "", "TLS certificated")
flag.String("tls-key", "", "TLS private key")

flag.Bool("ignore-test", false, "ignore test=True events")
flag.Bool("ignore-mock", false, "ignore mock=True events")

flag.Bool("health-minions", defaultHealthMinion, "enable minion metrics")
flag.String("health-functions-filter", defaultHealthStatesFilter,
"apply filter on functions to monitor, separated by a comma")
flag.String("health-states-filter", defaultHealthStatesFilter,
"apply filter on states to monitor, separated by a comma")
flag.Parse()
}

func setDefaults() {
viper.SetDefault("log-level", defaultLogLevel)
viper.SetDefault("listen-port", defaultPort)
viper.SetDefault("metrics.health-minions", defaultHealthMinion)
viper.SetDefault("metrics.salt_function_status.filters.functions", []string{defaultHealthFunctionsFilter})
viper.SetDefault("metrics.salt_function_status.filters.states", []string{defaultHealthStatesFilter})
}

func getConfig() (Config, error) {
setDefaults()

// bind flags
var allFlags []viperFlag
flag.Visit(func(f *flag.Flag) {
m := viperFlag{original: *f, alias: flagConfigMapping[f.Name]}
allFlags = append(allFlags, m)
})

fSet := viperFlagSet{
flags: allFlags,
}
if err := viper.BindFlagValues(fSet); err != nil {
return Config{}, fmt.Errorf("flag binding failure: %w", err)
}

// bind configuration file
viper.SetConfigName("config")
viper.SetConfigType("yml")
viper.AddConfigPath(".")

err := viper.ReadInConfig()
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return Config{}, fmt.Errorf("invalid config file: %w", err)
}

// extract configuration
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return Config{}, fmt.Errorf("failed to load configuration: %w", err)
}

return cfg, nil
}

func checkRequirements(cfg Config) error {
if cfg.TLS.Enabled {
if cfg.TLS.Certificate == "" {
return errors.New("TLS Certificate not specified")
}
if cfg.TLS.Key == "" {
return errors.New("TLS Private Key not specified")
}
}

return nil
}

func ReadConfig() (Config, error) {
var err error

parseFlags()

cfg, err := getConfig()
if err != nil {
return Config{}, err
}

err = checkRequirements(cfg)
if err != nil {
return Config{}, err
}

return cfg, nil
}
42 changes: 42 additions & 0 deletions cmd/salt-exporter/flag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"flag"
"reflect"

"github.com/spf13/viper"
)

type viperFlag struct {
original flag.Flag
alias string
}

func (f viperFlag) HasChanged() bool { return true } // TODO: fix?

func (f viperFlag) Name() string {
if f.alias != "" {
return f.alias
}
return f.original.Name
}

func (f viperFlag) ValueString() string { return f.original.Value.String() }

func (f viperFlag) ValueType() string {
t := reflect.TypeOf(f.original.Value)
if t.Kind() == reflect.Ptr {
return t.Elem().Kind().String()
}
return t.Kind().String()
}

type viperFlagSet struct {
flags []viperFlag
}

func (f viperFlagSet) VisitAll(fn func(viper.FlagValue)) {
for _, flag := range f.flags {
fn(flag)
}
}
81 changes: 27 additions & 54 deletions cmd/salt-exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ package main

import (
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"syscall"

"github.com/kpetremann/salt-exporter/internal/logging"
Expand All @@ -29,65 +27,26 @@ func quit() {
log.Warn().Msg("Bye.")
}

func main() {
defer quit()

listenAddress := flag.String("host", "", "listen address")
listenPort := flag.Int("port", 2112, "listen port")
tlsEnabled := flag.Bool("tls", false, "enable TLS")
tlsCert := flag.String("tls-cert", "", "TLS certificated")
tlsKey := flag.String("tls-key", "", "TLS private key")
healthMinions := flag.Bool("health-minions", true, "Enable minion metrics")
healthFunctionsFilters := flag.String("health-functions-filter", "state.highstate",
"Apply filter on functions to monitor, separated by a comma")
healthStatesFilters := flag.String("health-states-filter", "highstate",
"Apply filter on states to monitor, separated by a comma")
ignoreTest := flag.Bool("ignore-test", false, "ignore test=True events")
ignoreMock := flag.Bool("ignore-mock", false, "ignore mock=True events")
logLevel := flag.String("log-level", "info", "log level (debug, info, warn, error, fatal, panic, disabled)")
flag.Parse()

logging.ConfigureLogging(*logLevel)

if *tlsEnabled {
missingFlag := false
if *tlsCert == "" {
missingFlag = true
log.Error().Msg("TLS certificate not specified")
}
if *tlsCert == "" {
missingFlag = true
log.Error().Msg("TLS private key not specified")
}
if missingFlag {
return
}
}

func printInfo(config Config) {
log.Info().Str("Version", version).Send()
log.Info().Str("Commit", commit).Send()
log.Info().Str("Build time", date).Send()

metricsConfig := metrics.MetricsConfig{
HealthMinions: *healthMinions,
HealthFunctionsFilters: strings.Split(*healthFunctionsFilters, ","),
HealthStatesFilters: strings.Split(*healthStatesFilters, ","),
IgnoreTest: *ignoreTest,
IgnoreMock: *ignoreMock,
}

if metricsConfig.HealthMinions {
log.Info().Msg("health-minions: metrics are enabled")
log.Info().Msgf("health-minions: functions filters: %s", *healthFunctionsFilters)
log.Info().Msgf("health-minions: states filters: %s", *healthStatesFilters)
if config.Metrics.HealthMinions {
log.Info().Msgf("health-minions: functions filters: %s", config.Metrics.SaltFunctionStatus.Filters.Functions)
log.Info().Msgf("health-minions: states filters: %s", config.Metrics.SaltFunctionStatus.Filters.States)
}

if metricsConfig.IgnoreTest {
if config.Metrics.Global.Filters.IgnoreTest {
log.Info().Msg("test=True events will be ignored")
}
if config.Metrics.Global.Filters.IgnoreMock {
log.Info().Msg("mock=True events will be ignored")
}
}

listenSocket := fmt.Sprint(*listenAddress, ":", *listenPort)
func start(config Config) {
listenSocket := fmt.Sprint(config.ListenAddress, ":", config.ListenPort)

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
Expand All @@ -100,7 +59,7 @@ func main() {
eventListener := listener.NewEventListener(ctx, parser, eventChan)

go eventListener.ListenEvents()
go metrics.ExposeMetrics(ctx, eventChan, metricsConfig)
go metrics.ExposeMetrics(ctx, eventChan, config.Metrics)

// start http server
log.Info().Msg("exposing metrics on " + listenSocket + "/metrics")
Expand All @@ -112,10 +71,10 @@ func main() {
go func() {
var err error

if !*tlsEnabled {
if !config.TLS.Enabled {
err = httpServer.ListenAndServe()
} else {
err = httpServer.ListenAndServeTLS(*tlsCert, *tlsKey)
err = httpServer.ListenAndServeTLS(config.TLS.Certificate, config.TLS.Key)
}

if err != nil {
Expand All @@ -130,3 +89,17 @@ func main() {
log.Error().Err(err).Send()
}
}

func main() {
defer quit()
logging.Configure()

config, err := ReadConfig()
if err != nil {
log.Fatal().Err(err).Msg("failed to load settings during initialization")
}

logging.SetLevel(config.LogLevel)
printInfo(config)
start(config)
}
16 changes: 14 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/prometheus/client_model v0.3.0
github.com/prometheus/common v0.42.0
github.com/rs/zerolog v1.29.1
github.com/spf13/viper v1.16.0
github.com/vmihailenco/msgpack/v5 v5.3.5
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -24,26 +25,37 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
Loading

0 comments on commit 0b9ebc2

Please sign in to comment.