-
Notifications
You must be signed in to change notification settings - Fork 2
/
serve.go
129 lines (117 loc) · 3.51 KB
/
serve.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
package cmd
import (
"net/url"
"github.com/fsnotify/fsnotify"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/loicalbertin/sweetcher/pkg/proxy"
)
var server *proxy.Server
func init() {
serveCmd := &cobra.Command{
Use: "serve",
Short: "runs a Sweetcher server",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
conf, err := initConfig()
if err != nil {
return err
}
profile, err := generateProfile(conf)
if err != nil {
return err
}
server = &proxy.Server{Addr: conf.Server.Address}
server.SetupProfile(profile)
viper.WatchConfig()
viper.OnConfigChange(updateConfigOnChangeEvent)
log.WithField("config", conf).Debugln("Running sweetcher server")
return server.ListenAndServe()
},
}
RootCmd.AddCommand(serveCmd)
}
func updateConfigOnChangeEvent(e fsnotify.Event) {
logger := log.WithFields(log.Fields{
"file": e.Name,
// "operation": e.Op,
})
logger.Info("reloading config file")
c := &Config{}
err := viper.Unmarshal(c)
if err != nil {
logger.WithField("error", err).Error("Failed to read config file")
return
}
logger = logger.WithField("profile", c.Server.Profile)
setupLogs(c)
profile, err := generateProfile(c)
if err != nil {
logger.WithField("error", err).Error("Failed to create profile from config file")
return
}
server.SetupProfile(profile)
logger.Info("Profile reloaded")
}
func initConfig() (*Config, error) {
viper.SetConfigName("sweetcher") // name of config file (without extension)
viper.AddConfigPath(".") // path to look for the config file in
viper.AddConfigPath("$HOME/.sweetcher") // call multiple times to add many search paths
viper.AddConfigPath("/etc/sweetcher/") // optionally look for config in the working directory
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
return nil, errors.Errorf("Fatal error config file: %s", err)
}
conf := &Config{}
viper.Unmarshal(conf)
setupLogs(conf)
return conf, nil
}
func generateProfile(cfg *Config) (*proxy.Profile, error) {
proxies := make(map[string]*url.URL)
for proxyName, proxyURL := range cfg.Proxies {
p, err := url.Parse(proxyURL)
if err != nil {
return nil, errors.Wrapf(err, "Malformed proxy definition for proxy %q", proxyName)
}
proxies[proxyName] = p
}
// Defaults to direct proxy
profile := &proxy.Profile{}
if cfg.Server.Profile == "direct" {
return profile, nil
}
p, ok := cfg.Profiles[cfg.Server.Profile]
if !ok {
return nil, errors.Errorf("specified server profile %q not found", cfg.Server.Profile)
}
def, ok := proxies[p.Default]
if !ok && p.Default != "direct" {
return nil, errors.Errorf("specified default proxy %q not found for profile %q", p.Default, cfg.Server.Profile)
}
profile.Default = def
for _, r := range p.Rules {
rp, ok := proxies[r.Proxy]
if !ok && r.Proxy != "direct" {
return nil, errors.Errorf("specified proxy %q not found for rule %q in profile %q", r.Proxy, r.HostWildcard, cfg.Server.Profile)
}
profile.Rules = append(profile.Rules, proxy.Rule{Pattern: r.HostWildcard, Proxy: rp})
}
return profile, nil
}
func setupLogs(c *Config) {
level := c.Server.Logs.Level
if level == "" {
level = "info"
}
l, err := log.ParseLevel(level)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Fatal("failed to parse config file log level")
}
log.SetLevel(l)
log.SetFormatter(&log.TextFormatter{})
}