Skip to content

Commit 1c29eed

Browse files
Merge pull request #26 from stefanotorresi/config-refactoring
refactor global config into a dedicated internal package
2 parents b675fa4 + 9358cb2 commit 1c29eed

File tree

4 files changed

+127
-66
lines changed

4 files changed

+127
-66
lines changed

internal/config/config.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package config
2+
3+
import (
4+
"net/url"
5+
"regexp"
6+
7+
"github.com/pkg/errors"
8+
flag "github.com/spf13/pflag"
9+
"github.com/spf13/viper"
10+
)
11+
12+
func New() (*viper.Viper, error) {
13+
config := viper.New()
14+
15+
err := config.BindPFlags(flag.CommandLine)
16+
if err != nil {
17+
return nil, errors.Wrap(err, "could not bind config to CLI flags")
18+
}
19+
20+
// try to get the "config" value from the bound "config" CLI flag
21+
path := config.GetString("config")
22+
if path != "" {
23+
// try to manually load the configuration from the given path
24+
err = loadConfigurationFromFile(config, path)
25+
} else {
26+
// otherwise try viper's auto-discovery
27+
err = loadConfigurationAutomatically(config)
28+
}
29+
30+
if err != nil {
31+
return nil, errors.Wrap(err, "could not load configuration file")
32+
}
33+
34+
setLogLevel(config.GetString("log-level"))
35+
36+
err = validateSapControlUrl(config)
37+
if err != nil {
38+
return nil, errors.Wrap(err, "invalid configuration value")
39+
}
40+
41+
sanitizeSapControlUrl(config)
42+
43+
return config, nil
44+
}
45+
46+
// returns an error in case the sap-control-url config value cannot be parsed as URL
47+
func validateSapControlUrl(config *viper.Viper) error {
48+
sapControlUrl := config.GetString("sap-control-url")
49+
if _, err := url.ParseRequestURI(sapControlUrl); err != nil {
50+
return errors.Wrap(err, "invalid config value for sap-control-url")
51+
}
52+
return nil
53+
}
54+
55+
// automatically adds an http:// prefix in case it's missing from the value, to avoid the downstream consumer
56+
// throw errors due to missing schema URL component
57+
func sanitizeSapControlUrl(config *viper.Viper) {
58+
sapControlUrl := config.GetString("sap-control-url")
59+
hasScheme, _ := regexp.MatchString("^https?://", sapControlUrl)
60+
if !hasScheme {
61+
sapControlUrl = "http://" + sapControlUrl
62+
config.Set("sap-control-url", sapControlUrl)
63+
}
64+
}

internal/config/loader.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package config
2+
3+
import (
4+
"os"
5+
6+
"github.com/pkg/errors"
7+
log "github.com/sirupsen/logrus"
8+
"github.com/spf13/viper"
9+
)
10+
11+
func loadConfigurationAutomatically(config *viper.Viper) error {
12+
config.SetConfigName("sap_host_exporter")
13+
config.AddConfigPath("./")
14+
config.AddConfigPath("$HOME/.config/")
15+
config.AddConfigPath("/etc/")
16+
config.AddConfigPath("/usr/etc/")
17+
18+
err := config.ReadInConfig()
19+
if err == nil {
20+
log.Info("Using config file: ", config.ConfigFileUsed())
21+
return nil
22+
}
23+
24+
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
25+
log.Infof("Could not discover configuration file: %s", err)
26+
log.Info("Default configuration values will be used")
27+
return nil
28+
}
29+
30+
return errors.Wrap(err, "could not load automatically discovered config file")
31+
}
32+
33+
// loads configuration from an explicit file path
34+
func loadConfigurationFromFile(config *viper.Viper, path string) error {
35+
// we hard-code the config type to yaml, otherwise ReadConfig will not load the values
36+
// see https://github.com/spf13/viper/issues/316
37+
config.SetConfigType("yaml")
38+
39+
file, err := os.Open(path)
40+
if err != nil {
41+
return errors.Wrap(err, "could not open file")
42+
}
43+
defer file.Close()
44+
45+
err = config.ReadConfig(file)
46+
if err != nil {
47+
return errors.Wrap(err, "could not read file")
48+
}
49+
50+
return nil
51+
}

internal/log.go renamed to internal/config/log.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package internal
1+
package config
22

33
import log "github.com/sirupsen/logrus"
44

5-
func SetLogLevel(level string) {
5+
func setLogLevel(level string) {
66
switch level {
77
case "error":
88
log.SetLevel(log.ErrorLevel)

main.go

+10-64
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,42 @@
11
package main
22

33
import (
4-
"bytes"
54
"fmt"
6-
"io/ioutil"
75
"net/http"
86

97
"github.com/hooklift/gowsdl/soap"
108
"github.com/prometheus/client_golang/prometheus"
119
"github.com/prometheus/client_golang/prometheus/promhttp"
1210
log "github.com/sirupsen/logrus"
1311
flag "github.com/spf13/pflag"
14-
config "github.com/spf13/viper"
1512

1613
"github.com/SUSE/sap_host_exporter/collector/alert"
1714
"github.com/SUSE/sap_host_exporter/collector/dispatcher"
1815
"github.com/SUSE/sap_host_exporter/collector/enqueue_server"
1916
"github.com/SUSE/sap_host_exporter/collector/start_service"
2017
"github.com/SUSE/sap_host_exporter/internal"
18+
"github.com/SUSE/sap_host_exporter/internal/config"
2119
"github.com/SUSE/sap_host_exporter/internal/sapcontrol"
2220
)
2321

2422
func init() {
25-
config.SetConfigName("sap_host_exporter")
26-
config.AddConfigPath("./")
27-
config.AddConfigPath("$HOME/.config/")
28-
config.AddConfigPath("/etc/")
29-
config.AddConfigPath("/usr/etc/")
30-
3123
flag.String("port", "9680", "The port number to listen on for HTTP requests")
3224
flag.String("address", "0.0.0.0", "The address to listen on for HTTP requests")
3325
flag.String("log-level", "info", "The minimum logging level; levels are, in ascending order: debug, info, warn, error")
34-
flag.String("sap-control-url", "", "The URL of the SAPControl SOAP web service, e.g. http://$HOST:$PORT")
35-
flag.String("config", "", "The path where a custom configuration.yaml file is located. NOTE: the conf must be yaml")
36-
37-
err := config.BindPFlags(flag.CommandLine)
38-
if err != nil {
39-
log.Errorf("Could not bind config to CLI flags: %v", err)
40-
}
26+
flag.String("sap-control-url", "localhost:50013", "The URL of the SAPControl SOAP web service, e.g. $HOST:$PORT")
27+
flag.StringP("config", "c", "", "The path to a custom configuration file. NOTE: it must be in yaml format.")
4128
}
4229

4330
func main() {
44-
initConfig()
45-
4631
var err error
4732

33+
flag.Parse()
34+
35+
config, err := config.New()
36+
if err != nil {
37+
log.Fatalf("Could not initialize config: %s", err)
38+
}
39+
4840
client := soap.NewClient(
4941
config.GetString("sap-control-url"),
5042
soap.WithBasicAuth(
@@ -99,49 +91,3 @@ func main() {
9991
log.Infof("Serving metrics on %s", fullListenAddress)
10092
log.Fatal(http.ListenAndServe(fullListenAddress, nil))
10193
}
102-
103-
func initConfig() {
104-
105-
flag.Parse()
106-
107-
// read configuration from custom path or defaults
108-
readExporterConf()
109-
110-
internal.SetLogLevel(config.GetString("log-level"))
111-
112-
if config.GetString("sap-control-url") == "" {
113-
log.Fatal("sap-control-url cannot be empty, please use the --sap-control-url flag or set a value in the config")
114-
}
115-
}
116-
117-
func readExporterConf() {
118-
119-
// read first the configuration from custom file. If not provided, read default.
120-
confFile := config.GetString("config")
121-
if confFile != "" {
122-
// hardcode for custom config file type to yaml
123-
// this workaround is needed otherwise viper return empty conf
124-
// see issue https://github.com/spf13/viper/issues/316
125-
config.SetConfigType("yaml")
126-
confData, err := ioutil.ReadFile(confFile)
127-
if err != nil {
128-
log.Fatal("Could not read configuration file for exporter: ", err)
129-
}
130-
config.ReadConfig(bytes.NewBuffer(confData))
131-
if err != nil {
132-
log.Fatal("Could not parse configuration:", err)
133-
}
134-
log.Info("Using custom configuration file provided by flag")
135-
return
136-
}
137-
138-
// if no custom file given, read configuration from default paths
139-
err := config.ReadInConfig()
140-
if err != nil {
141-
log.Warn(err)
142-
log.Info("Default config values will be used")
143-
} else {
144-
log.Info("Using config file: ", config.ConfigFileUsed())
145-
}
146-
147-
}

0 commit comments

Comments
 (0)