-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
147 lines (130 loc) · 4.63 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
package config
import (
"fmt"
"io/ioutil"
"os"
"fornaxian.tech/log"
"github.com/BurntSushi/toml"
)
// Manager is in charge of finding and reading configuration files
type Manager struct {
confPaths []string
fileName string
defaultConfig string
Conf interface{}
}
// ErrNoConfigFound returned by ReloadConfig if no config file cound be found
type ErrNoConfigFound struct{}
func (ErrNoConfigFound) Error() string {
return "no config files found at the configured locations"
}
// New prepares a new configuration manager which can be used to read properties
// from a TOML config file. The confDir param can be used to set a custom config
// directory. If it's left empty the default config locations will be used. When
// no config files can be found on the system a new one will be generated in the
// current working directory, and the program will exit. If it fails to write
// the config file it will print instructions for the user to create a new
// config file and exit with an error status.
//
// Params:
// - defaultConfig: The default configuration file in TOML format. If a config
// file is found at any of the configured locations, but it's
// missing some tags the default values from this config will
// be used. Note that if no config files are found, all the
// properties will be the defaults.
// - confDir: A directory which will be searched for a configuration file.
// If empty the default system directories will be searched for
// configuration files.
// - fileName: The name of the configuration file, only files with this
// name will be attempted to be parsed.
// - conf: This has to be a pointer to a struct with TOML annotations.
// https://github.com/BurntSushi/toml/blob/master/README.md#examples
// - autoload: Setting this to true will automatically load the config file
// before returning this function. If it fails to load the
// config file it will print instructions for the user to
// stdout and exit the program. If you wish to handle this
// behaviour yourself you should set autoload to false and call
// Manager.LoadConfig manually.
func New(
defaultConf, confDir, fileName string,
config interface{},
autoload bool,
) (*Manager, error) {
var confPaths = []string{}
if confDir != "" {
confPaths = append(confPaths, confDir+"/"+fileName)
}
confPaths = append(
confPaths,
fileName, // In the working directory
fmt.Sprintf("%s/.config/%s", os.Getenv("HOME"), fileName),
"/usr/local/etc/"+fileName,
"/etc/"+fileName,
)
var err error
var c = &Manager{
confPaths: confPaths,
fileName: fileName,
defaultConfig: defaultConf,
Conf: config,
}
// Read the default configuration. The values entered in the config file
// will overwrite the defaults
_, err = toml.Decode(defaultConf, c.Conf)
if err != nil {
return nil, fmt.Errorf("failed to decode default config: %s", err)
}
if !autoload {
return c, nil
}
err = c.LoadConfig()
if _, ok := err.(ErrNoConfigFound); ok {
log.Info("No configuration files were found, a new one will be " +
"generated in the present working directory")
err = ioutil.WriteFile(fileName, []byte(defaultConf), 0644)
if err != nil {
log.Warn(
"A default config file could not be created in this directory "+
"for the following reason: %s.\n\n"+
"Please manually create a configuration file in one "+
"of the following places:\n", err, fileName,
)
for _, cd := range c.confPaths {
fmt.Println(cd)
}
os.Exit(1)
}
os.Exit(0)
}
log.Info("Successfully loaded configuration file")
return c, nil
}
// LoadConfig tries every configuration file configured in the Manager until it
// finds one it can read. If no configuration files can be read it will return
// an ErrNoConfigFound error. If error is nil the config was loaded
// successfully. This function can be called multiple times to reload the config
// file from disk.
func (c *Manager) LoadConfig() error {
var confStr []byte
var err error
for _, cd := range c.confPaths {
if cd == "" {
continue
}
log.Debug("Trying configuration file '%s'", cd)
confStr, err = ioutil.ReadFile(cd)
if err != nil {
log.Debug("No config found at '%s' (%s)", cd, err)
continue
}
// Reading succeeded, now try decoding
_, err = toml.Decode(string(confStr), c.Conf)
if err != nil {
log.Warn("Unable to decode config file at '%s': %s", cd, err)
continue
}
// We did it
return nil
}
return ErrNoConfigFound{}
}