-
Notifications
You must be signed in to change notification settings - Fork 1
/
initializer.go
162 lines (137 loc) · 4.38 KB
/
initializer.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
package config
import (
"net/http"
"sync"
"time"
"github.com/getlantern/golog"
"github.com/getlantern/yaml"
"github.com/getlantern/flashlight/v7/common"
)
const packageLogPrefix = "flashlight.config"
var (
log = golog.LoggerFor(packageLogPrefix)
// DefaultGlobalConfigPollInterval determines how frequently to fetch global.yaml
DefaultGlobalConfigPollInterval = 1 * time.Hour
)
// Init determines the URLs at which to fetch global config and passes those to InitWithURLs, which
// initializes the config setup for fetching the global config. It returns a function that can be
// used to stop the reading of configs.
func Init(
configDir string, flags map[string]interface{}, userConfig common.UserConfig,
origGlobalDispatch func(interface{}, Source), onGlobalSaveError func(error),
rt http.RoundTripper) (stop func()) {
staging := isStaging(flags)
globalConfigURL := checkOverrides(flags, getGlobalURL(staging), "global.yaml.gz")
return InitWithURLs(
configDir, flags, userConfig,
origGlobalDispatch, onGlobalSaveError, globalConfigURL, rt)
}
type cfgWithSource struct {
cfg interface{}
src Source
}
// InitWithURLs initializes the config setup for fetching the global config from the given URL. It
// returns a function that can be used to stop the reading of configs.
func InitWithURLs(
configDir string, flags map[string]interface{}, userConfig common.UserConfig,
origGlobalDispatch func(interface{}, Source), onGlobalSaveError func(error),
globalURL string, rt http.RoundTripper,
) (stop func()) {
var mx sync.RWMutex
globalConfigPollInterval := DefaultGlobalConfigPollInterval
globalDispatchCh := make(chan cfgWithSource)
go func() {
for c := range globalDispatchCh {
origGlobalDispatch(c.cfg, c.src)
}
}()
globalDispatch := func(cfg interface{}, src Source) {
globalConfig, ok := cfg.(*Global)
if ok {
mx.Lock()
if globalConfig.GlobalConfigPollInterval > 0 {
globalConfigPollInterval = globalConfig.GlobalConfigPollInterval
}
mx.Unlock()
}
// Rather than call `origGlobalDispatch` here, we are calling it in a
// separate goroutine (initiated above) that listens for messages on
// `globalDispatchCh`. This (a) avoids blocking Lantern startup when
// applying new configuration and (b) allows us to serialize application of
// config changes.
globalDispatchCh <- cfgWithSource{cfg, src}
}
// These are the options for fetching the global config.
globalOptions := &options{
saveDir: configDir,
onSaveError: onGlobalSaveError,
obfuscate: obfuscate(flags),
name: "global.yaml",
originURL: globalURL,
userConfig: userConfig,
unmarshaler: newGlobalUnmarshaler(flags),
dispatch: globalDispatch,
sleep: func() time.Duration {
mx.RLock()
defer mx.RUnlock()
return globalConfigPollInterval
},
sticky: isSticky(flags),
rt: rt,
opName: "fetch_global",
ignoreSaved: true,
}
stopGlobal := pipeConfig(globalOptions)
return func() {
log.Debug("*************** Stopping Config")
stopGlobal()
}
}
func newGlobalUnmarshaler(flags map[string]interface{}) func(bytes []byte) (interface{}, error) {
return func(bytes []byte) (interface{}, error) {
gl := NewGlobal()
gl.applyFlags(flags)
if err := yaml.Unmarshal(bytes, gl); err != nil {
return nil, err
}
if err := gl.validate(); err != nil {
return nil, err
}
return gl, nil
}
}
func obfuscate(flags map[string]interface{}) bool {
return flags["readableconfig"] == nil || !flags["readableconfig"].(bool)
}
func isStaging(flags map[string]interface{}) bool {
return checkBool(flags, "staging")
}
func isSticky(flags map[string]interface{}) bool {
return checkBool(flags, "stickyconfig")
}
func checkBool(flags map[string]interface{}, key string) bool {
if s, ok := flags[key].(bool); ok {
return s
}
return false
}
func checkOverrides(flags map[string]interface{},
url string, name string) string {
if s, ok := flags["cloudconfig"].(string); ok {
if len(s) > 0 {
log.Debugf("Overridding config URL from the command line '%v'", s)
return s + "/" + name
}
}
return url
}
// getGlobalURL returns the global URL to use depending on whether or not
// we're in staging.
func getGlobalURL(staging bool) string {
if staging {
log.Debug("Will obtain global.yaml from staging service")
return common.GlobalStagingURL
}
log.Debugf("Will obtain global.yaml from production service at %v", common.GlobalURL)
return common.GlobalURL
}