-
Notifications
You must be signed in to change notification settings - Fork 9
/
main.go
187 lines (158 loc) · 4.53 KB
/
main.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
package main
import (
"flag"
"fmt"
"net/http"
"os"
"path"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/fleaz/CptHook/input"
"github.com/spf13/viper"
)
var (
inputChannel = make(chan input.IRCMessage, 30)
version = "dev"
commit = "none"
date = time.Now().Format(time.RFC3339)
)
func configureLogLevel() {
if l := viper.GetString("logging.level"); l != "" {
level, err := log.ParseLevel(l)
if err != nil {
log.WithFields(log.Fields{
"level": l,
}).Fatal("Uknown loglevel defined in configuration.")
}
log.WithFields(log.Fields{
"level": level,
}).Info("Setting loglevel defined by configuration")
log.SetLevel(level)
return
}
log.Info("Loglevel not defined in configuration. Defaulting to ERROR")
log.SetLevel(log.ErrorLevel)
}
type Configuration struct {
Modules map[string]InputModule `yaml:"modules"`
}
type InputModule struct {
Type string `yaml:"type"`
Endpoint string `yaml:"endpoint"`
}
func createModuleObject(name string) (input.Module, error) {
var m input.Module
var e error
switch name {
case "gitlab":
m = &input.GitlabModule{}
case "prometheus":
m = &input.PrometheusModule{}
case "simple":
m = &input.SimpleModule{}
case "icinga2":
m = &input.Icinga2Module{}
default:
e = fmt.Errorf("ignoring configuration for unknown module: %q", name)
}
return m, e
}
func validateConfig(c Configuration) {
var foundErrors []string
for blockName, blockConfig := range c.Modules {
if blockConfig.Type == "" {
foundErrors = append(foundErrors, fmt.Sprintf("Block %q is missing its type", blockName))
}
if blockConfig.Endpoint == "" {
foundErrors = append(foundErrors, fmt.Sprintf("Block %q is missing its endpoint", blockName))
}
}
if len(foundErrors) > 0 {
log.Error("Found the following errors in the configuration:")
for _, e := range foundErrors {
log.Error(e)
}
os.Exit(1)
} else {
log.Info("Configuration parsed without errors")
}
}
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.WithFields(log.Fields{
"remote": r.RemoteAddr,
"method": r.Method,
"host": r.Host,
"uri": r.URL,
}).Debug("Received HTTP request")
next.ServeHTTP(w, r)
})
}
func ircCheckMiddleware(next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !client.IsConnected() {
log.WithFields(log.Fields{
"remote": r.RemoteAddr,
"uri": r.URL,
}).Warn("IRC server is disconnected. Dropping incoming HTTP request")
// In some weird situations the IsConnected function detects that we are no longer connected,
// but the reconnect logic in irc.go doesn't detects the connection problem and won't reconnect
// Therefore if we detect that problem here, we Close() the connection manually and force a reconenct
client.Close()
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("IRC server disconnected"))
return
}
next.ServeHTTP(w, r)
})
}
func main() {
confDirPtr := flag.String("config", "/etc/cpthook.yml", "Path to the configfile")
flag.Parse()
// Load configuration from file
confDir, confName := path.Split(*confDirPtr)
viper.SetConfigName(strings.Split(confName, ".")[0])
if len(confDir) > 0 {
viper.AddConfigPath(confDir)
} else {
viper.AddConfigPath(".")
}
err := viper.ReadInConfig()
if err != nil {
log.Fatal(err)
}
configureLogLevel()
var channelList = []string{}
var config = Configuration{}
err = viper.Unmarshal(&config)
if err != nil {
log.Fatal(err)
}
validateConfig(config)
for blockName, blockConfig := range config.Modules {
module, err := createModuleObject(blockConfig.Type)
if err != nil {
log.Warn(err)
continue
}
log.Infof("Loaded block %q from config (Type %q, Endpoint %q)", blockName, blockConfig.Type, blockConfig.Endpoint)
configPath := fmt.Sprintf("modules.%s", blockName)
module.Init(viper.Sub(configPath), &inputChannel)
channelList = append(channelList, module.GetChannelList()...)
http.HandleFunc(blockConfig.Endpoint, loggingMiddleware(ircCheckMiddleware(module.GetHandler())))
}
// Start IRC connection
go ircConnection(viper.Sub("irc"), channelList)
// Start HTTP server
srv := &http.Server{
Addr: viper.GetString("http.listen"),
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
}
srv.SetKeepAlivesEnabled(false)
log.WithFields(log.Fields{
"listen": viper.GetString("http.listen"),
}).Info("Started HTTP Server")
log.Fatal(srv.ListenAndServe(), nil)
}