forked from infinilabs/framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
337 lines (284 loc) · 8.37 KB
/
config.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
//Package config , actually copied from github.com/elastic/beats
package config
import (
"errors"
"flag"
log "github.com/cihub/seelog"
"github.com/elastic/go-ucfg"
"github.com/elastic/go-ucfg/cfgutil"
cfgflag "github.com/elastic/go-ucfg/flag"
"github.com/elastic/go-ucfg/yaml"
)
// Config object to store hierarchical configurations into.
// See https://godoc.org/github.com/elastic/go-ucfg#Config
type Config ucfg.Config
// Namespace storing at most one configuration section by name and sub-section.
type Namespace struct {
C map[string]*Config `config:",inline"`
}
type flagOverwrite struct {
config *ucfg.Config
path string
value string
}
var configOpts = []ucfg.Option{
ucfg.PathSep("."),
ucfg.ResolveEnv,
ucfg.VarExp,
}
// NewConfig create a pretty new config
func NewConfig() *Config {
return fromConfig(ucfg.New())
}
// NewConfigFrom get config instance
func NewConfigFrom(from interface{}) (*Config, error) {
c, err := ucfg.NewFrom(from, configOpts...)
return fromConfig(c), err
}
// MergeConfigs just merge configs together
func MergeConfigs(cfgs ...*Config) (*Config, error) {
config := NewConfig()
for _, c := range cfgs {
if err := config.Merge(c); err != nil {
return nil, err
}
}
return config, nil
}
// NewConfigWithYAML load config from yaml
func NewConfigWithYAML(in []byte, source string) (*Config, error) {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: source}),
},
configOpts...,
)
c, err := yaml.NewConfig(in, opts...)
return fromConfig(c), err
}
// NewFlagConfig will use flags
func NewFlagConfig(
set *flag.FlagSet,
def *Config,
name string,
usage string,
) *Config {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
},
configOpts...,
)
var to *ucfg.Config
if def != nil {
to = def.access()
}
config := cfgflag.ConfigVar(set, to, name, usage, opts...)
return fromConfig(config)
}
// NewFlagOverwrite will use flags which specified
func NewFlagOverwrite(
set *flag.FlagSet,
config *Config,
name, path, def, usage string,
) *string {
if config == nil {
panic("Missing configuration")
}
if path == "" {
panic("empty path")
}
if def != "" {
err := config.SetString(path, -1, def)
if err != nil {
panic(err)
}
}
f := &flagOverwrite{
config: config.access(),
path: path,
value: def,
}
if set == nil {
flag.Var(f, name, usage)
} else {
set.Var(f, name, usage)
}
return &f.value
}
// LoadFile will load config from specify file
func LoadFile(path string) (*Config, error) {
c, err := yaml.NewConfigWithFile(path, configOpts...)
if err != nil {
return nil, err
}
cfg := fromConfig(c)
log.Debugf("load config file '%v'", path)
return cfg, err
}
// LoadFiles will load configs from specify files
func LoadFiles(paths ...string) (*Config, error) {
merger := cfgutil.NewCollector(nil, configOpts...)
for _, path := range paths {
cfg, err := LoadFile(path)
if err := merger.Add(cfg.access(), err); err != nil {
return nil, err
}
}
return fromConfig(merger.Config()), nil
}
// Merge a map, a slice, a struct or another Config object into c.
func (c *Config) Merge(from interface{}) error {
return c.access().Merge(from, configOpts...)
}
// Unpack unpacks c into a struct, a map, or a slice allocating maps, slices,
// and pointers as necessary.
func (c *Config) Unpack(to interface{}) error {
return c.access().Unpack(to, configOpts...)
}
// Path gets the absolute path of c separated by sep. If c is a root-Config an
// empty string will be returned.
func (c *Config) Path() string {
return c.access().Path(".")
}
// PathOf gets the absolute path of a potential setting field in c with name
// separated by sep.
func (c *Config) PathOf(field string) string {
return c.access().PathOf(field, ".")
}
// HasField checks if c has a top-level named key name.
func (c *Config) HasField(name string) bool {
return c.access().HasField(name)
}
// CountField returns number of entries in a table or 1 if entry is a primitive value.
// Primitives settings can be handled like a list with 1 entry.
func (c *Config) CountField(name string) (int, error) {
return c.access().CountField(name)
}
// Bool reads a boolean setting returning an error if the setting has no
// boolean value.
func (c *Config) Bool(name string, idx int) (bool, error) {
return c.access().Bool(name, idx, configOpts...)
}
// Strings reads a string setting returning an error if the setting has
// no string or primitive value convertible to string.
func (c *Config) String(name string, idx int) (string, error) {
return c.access().String(name, idx, configOpts...)
}
// Int reads an int64 value returning an error if the setting is
// not integer value, the primitive value is not convertible to int or a conversion
// would create an integer overflow.
func (c *Config) Int(name string, idx int) (int64, error) {
return c.access().Int(name, idx, configOpts...)
}
// Float reads a float64 value returning an error if the setting is
// not a float value or the primitive value is not convertible to float.
func (c *Config) Float(name string, idx int) (float64, error) {
return c.access().Float(name, idx, configOpts...)
}
// Child returns a child configuration or an error if the setting requested is a
// primitive value only.
func (c *Config) Child(name string, idx int) (*Config, error) {
sub, err := c.access().Child(name, idx, configOpts...)
return fromConfig(sub), err
}
// SetBool sets a boolean primitive value. An error is returned if the new name
// is invalid.
func (c *Config) SetBool(name string, idx int, value bool) error {
return c.access().SetBool(name, idx, value, configOpts...)
}
// SetInt sets an integer primitive value. An error is returned if the new
// name is invalid.
func (c *Config) SetInt(name string, idx int, value int64) error {
return c.access().SetInt(name, idx, value, configOpts...)
}
// SetFloat sets an floating point primitive value. An error is returned if
// the name is invalid.
func (c *Config) SetFloat(name string, idx int, value float64) error {
return c.access().SetFloat(name, idx, value, configOpts...)
}
// SetString sets string value. An error is returned if the name is invalid.
func (c *Config) SetString(name string, idx int, value string) error {
return c.access().SetString(name, idx, value, configOpts...)
}
// SetChild adds a sub-configuration. An error is returned if the name is invalid.
func (c *Config) SetChild(name string, idx int, value *Config) error {
return c.access().SetChild(name, idx, value.access(), configOpts...)
}
// IsDict checks if c has named keys.
func (c *Config) IsDict() bool {
return c.access().IsDict()
}
// IsArray checks if c has index only accessible settings.
func (c *Config) IsArray() bool {
return c.access().IsArray()
}
// Enabled was a predefined config, enabled by default if no config was found
func (c *Config) Enabled(defaultV bool) bool {
testEnabled := struct {
Enabled bool `config:"enabled"`
}{defaultV}
if c == nil {
return defaultV
}
if err := c.Unpack(&testEnabled); err != nil {
// if unpacking fails, expect 'enabled' being set to default value
return defaultV
}
return testEnabled.Enabled
}
func fromConfig(in *ucfg.Config) *Config {
return (*Config)(in)
}
func (c *Config) access() *ucfg.Config {
return (*ucfg.Config)(c)
}
// GetFields returns a list of all top-level named keys in c.
func (c *Config) GetFields() []string {
return c.access().GetFields()
}
func (f *flagOverwrite) String() string {
return f.value
}
func (f *flagOverwrite) Set(v string) error {
opts := append(
[]ucfg.Option{
ucfg.MetaData(ucfg.Meta{Source: "command line flag"}),
},
configOpts...,
)
err := f.config.SetString(f.path, -1, v, opts...)
if err != nil {
return err
}
f.value = v
return nil
}
func (f *flagOverwrite) Get() interface{} {
return f.value
}
// Validate checks at most one sub-namespace being set.
func (ns *Namespace) Validate() error {
if len(ns.C) > 1 {
return errors.New("more then one namespace configured")
}
return nil
}
// Name returns the configuration sections it's name if a section has been set.
func (ns *Namespace) Name() string {
for name := range ns.C {
return name
}
return ""
}
// Config return the sub-configuration section if a section has been set.
func (ns *Namespace) Config() *Config {
for _, cfg := range ns.C {
return cfg
}
return nil
}
// IsSet returns true if a sub-configuration section has been set.
func (ns *Namespace) IsSet() bool {
return len(ns.C) != 0
}