-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathconfig.go
159 lines (132 loc) · 2.66 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
package config
import (
"encoding/json"
"errors"
"io/ioutil"
"log"
"os"
"os/signal"
"strconv"
"syscall"
)
// Config represents a configuration file.
type Config struct {
filename string
cache *map[string]interface{}
}
// New creates a new Config object.
func New(filename string) *Config {
config := Config{filename, nil}
config.Reload()
go config.watch()
return &config
}
// Get retreives a Config option into a passed in pointer or returns an error.
func (config *Config) Get(key string, v interface{}) error {
var val interface{}
env, set := os.LookupEnv(key)
if set {
switch v.(type) {
case *float64:
val, err := strconv.ParseFloat(env, 64)
if err != nil {
return err
}
*v.(*float64) = val
return nil
case *int:
val, err := strconv.ParseInt(env, 10, 64)
if err != nil {
return err
}
*v.(*int) = int(val)
return nil
default:
val = env
}
} else if config.cache != nil {
val = (*config.cache)[key]
}
// Cast JSON values
switch v.(type) {
case *string:
if val == nil {
val = ""
}
if b, ok := val.(bool); ok {
*v.(*string) = strconv.FormatBool(b)
} else if f, ok := val.(float64); ok {
*v.(*string) = strconv.FormatFloat(f, 'f', -1, 64)
} else {
*v.(*string) = val.(string)
}
case *bool:
switch val {
case nil, 0, false, "", "0", "false":
// falsey
val = false
default:
// truthy
val = true
}
*v.(*bool) = val.(bool)
case *float64:
if val == nil {
val = float64(0)
}
if s, ok := val.(string); ok {
pf, err := strconv.ParseFloat(s, 64)
if err != nil {
return err
}
*v.(*float64) = pf
} else {
*v.(*float64) = val.(float64)
}
case *int:
if val == nil {
val = float64(0)
}
*v.(*int) = int(val.(float64))
default:
return errors.New("Type not supported")
}
return nil
}
// Reload clears the config cache.
func (config *Config) Reload() error {
cache, err := primeCacheFromFile(config.filename)
config.cache = cache
if err != nil {
return err
}
return nil
}
func (config *Config) watch() {
l := log.New(os.Stderr, "", 0)
// Catch SIGHUP to automatically reload cache
sighup := make(chan os.Signal, 1)
signal.Notify(sighup, syscall.SIGHUP)
for {
<-sighup
l.Println("Caught SIGHUP, reloading config...")
config.Reload()
}
}
func primeCacheFromFile(file string) (*map[string]interface{}, error) {
// File exists?
if _, err := os.Stat(file); os.IsNotExist(err) {
return nil, err
}
// Read file
raw, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
// Unmarshal
var config map[string]interface{}
if err := json.Unmarshal(raw, &config); err != nil {
return nil, err
}
return &config, nil
}