-
Notifications
You must be signed in to change notification settings - Fork 0
/
goconfig.go
134 lines (117 loc) · 3.72 KB
/
goconfig.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package goconfig
import (
"fmt"
"reflect"
"strings"
"github.com/asaskevich/govalidator"
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"github.com/jacobweinstock/registrar"
"go.uber.org/zap"
"github.com/pkg/errors"
)
// Parser struct
type Parser struct {
Logger logr.Logger
Prefix string
File string
Usage func()
FlagInterface FlagParser
EnvInterface EnvParser
FileInterface *registrar.Registry
}
// NewParser parser struct
func NewParser(opts ...Option) *Parser {
log := defaultLogger()
c := &Parser{
Logger: log,
File: "config.yaml",
FlagInterface: new(gflags),
EnvInterface: new(envConfig),
FileInterface: registrar.NewRegistry(registrar.WithLogger(log)),
}
for _, opt := range opts {
opt(c)
}
// len of 0 means that no Registry with any registered drivers was passed in.
if len(c.FileInterface.Drivers) == 0 {
c.registerFileInterfaces()
}
return c
}
// Parse a config file, env vars and cli flags (override is in that order).
//
// The fields of the confStruct passed in must be exported (uppercase).
//
// CLI flags by default split camelCase field names with dashes.
// e.x. `KeyOne` would be a cli flag of `-key-one`.
// To modify this, add a struct tag.
// KeyOne string `flag:"keyone"` will give you a cli flag of `-keyone`.
func (c *Parser) Parse(confStruct interface{}) error {
// parse env then cli flags to get any config file path
err := ParseEnv(c.Logger, c.Prefix, confStruct, c.EnvInterface)
if err != nil {
return errors.WithMessage(err, "error parsing env vars")
}
//var f *gflags
err = ParseFlags(c.Logger, confStruct, c.FlagInterface)
if err != nil {
return errors.WithMessage(err, "error parsing cli flags")
}
// Read the config file
filename := getConfigValue(confStruct)
if filename == "" {
filename = c.File
}
err = ParseFileFromInterfaces(c.Logger, filename, confStruct, c.FileInterface.GetDriverInterfaces())
if err != nil {
c.Logger.V(1).Info("problem parsing file", "file", filename, "error", err.Error())
}
// Overwrite config with environment variables
err = ParseEnv(c.Logger, c.Prefix, confStruct, c.EnvInterface)
if err != nil {
return errors.WithMessage(err, "error parsing env vars")
}
// Overwrite config with command line args
err = ParseFlags(c.Logger, confStruct, c.FlagInterface)
if err != nil {
return errors.WithMessage(err, "error parsing cli flags")
}
return validateRequired(confStruct)
}
// validateRequired will look at the struct tags for a `valid:"required"` tag
// and make sure those fields have a value.
// If a default value is specified for a required field that will satisfy
func validateRequired(config interface{}) error {
_, err := govalidator.ValidateStruct(config)
return err
}
func (c *Parser) registerFileInterfaces() {
// register yaml file parser implementation
c.FileInterface.Register(fileInterfaceNameYaml, fileInterfaceProtocolYaml, fileInterfaceFeaturesYaml, nil, new(yamlParser))
// register json file parser implementation
c.FileInterface.Register(fileInterfaceNameJSON, fileInterfaceProtocolJSON, fileInterfaceFeaturesJSON, nil, new(jsonParser))
}
func getConfigValue(config interface{}) string {
val := reflect.ValueOf(config).Elem()
var name string
for i := 0; i < val.NumField(); i++ {
valueField := val.Field(i)
typeField := val.Type().Field(i)
if strings.ToLower(typeField.Name) == "config" {
name = valueField.Interface().(string)
break
}
}
return name
}
// defaultLogger is zap logr implementation
func defaultLogger() logr.Logger {
config := zap.NewProductionConfig()
config.OutputPaths = []string{"stdout"}
zapLogger, err := config.Build()
if err != nil {
panic(fmt.Sprintf("who watches the watchmen (%v)?", err))
}
return zapr.NewLogger(zapLogger)
}