Skip to content

Commit 1ee3eb4

Browse files
authored
Code cleanup of routes config loader and API server (#424)
1 parent 7d5bc8d commit 1ee3eb4

10 files changed

Lines changed: 302 additions & 385 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
release:
1111
uses: itzg/github-workflows/.github/workflows/go-with-releaser-image.yml@main
1212
with:
13-
go-version: "1.24.4"
13+
go-version-file: 'go.mod'
1414
enable-ghcr: true
1515
secrets:
1616
image-registry-username: ${{ secrets.DOCKERHUB_USERNAME }}

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ jobs:
1212
build:
1313
uses: itzg/github-workflows/.github/workflows/go-test.yml@main
1414
with:
15-
go-version: "1.24.4"
15+
go-version-file: 'go.mod'

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ The following shows a JSON file for routes config, where `default-server` can al
178178
}
179179
```
180180

181-
Sending a SIGHUP signal will cause mc-router to reload the routes config from disk.
181+
Sending a SIGHUP signal will cause mc-router to reload the routes config from disk. The file can also be watched for changes by setting `-routes-config-watch` or the env variable `ROUTES_CONFIG_WATCH` to "true".
182182

183183
## Auto Scale Allow/Deny List
184184

cmd/mc-router/main.go

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,6 @@ import (
1616
"github.com/sirupsen/logrus"
1717
)
1818

19-
type MetricsBackendConfig struct {
20-
Influxdb struct {
21-
Interval time.Duration `default:"1m"`
22-
Tags map[string]string `usage:"any extra tags to be included with all reported metrics"`
23-
Addr string
24-
Username string
25-
Password string
26-
Database string
27-
RetentionPolicy string
28-
}
29-
}
30-
3119
type WebhookConfig struct {
3220
Url string `usage:"If set, a POST request that contains connection status notifications will be sent to this HTTP address"`
3321
RequireUser bool `default:"false" usage:"Indicates if the webhook will only be called if a user is connecting rather than just server list/ping"`
@@ -62,11 +50,11 @@ type Config struct {
6250
DockerTimeout int `default:"0" usage:"Timeout configuration in seconds for the Docker integrations"`
6351
DockerRefreshInterval int `default:"15" usage:"Refresh interval in seconds for the Docker integrations"`
6452
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb,prometheus"`
65-
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
66-
ReceiveProxyProtocol bool `default:"false" usage:"Receive PROXY protocol from backend servers, by default trusts every proxy header that it receives, combine with -trusted-proxies to specify a list of trusted proxies"`
67-
TrustedProxies []string `usage:"Comma delimited list of CIDR notation IP blocks to trust when receiving PROXY protocol"`
68-
RecordLogins bool `default:"false" usage:"Log and generate metrics on player logins. Metrics only supported with influxdb or prometheus backend"`
69-
MetricsBackendConfig MetricsBackendConfig
53+
MetricsBackendConfig server.MetricsBackendConfig
54+
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
55+
ReceiveProxyProtocol bool `default:"false" usage:"Receive PROXY protocol from backend servers, by default trusts every proxy header that it receives, combine with -trusted-proxies to specify a list of trusted proxies"`
56+
TrustedProxies []string `usage:"Comma delimited list of CIDR notation IP blocks to trust when receiving PROXY protocol"`
57+
RecordLogins bool `default:"false" usage:"Log and generate metrics on player logins. Metrics only supported with influxdb or prometheus backend"`
7058
Routes RoutesConfig
7159
NgrokToken string `usage:"If set, an ngrok tunnel will be established. It is HIGHLY recommended to pass as an environment variable."`
7260
AutoScale AutoScale
@@ -133,7 +121,7 @@ func main() {
133121
ctx, cancel := context.WithCancel(context.Background())
134122
defer cancel()
135123

136-
metricsBuilder := NewMetricsBuilder(config.MetricsBackend, &config.MetricsBackendConfig)
124+
metricsBuilder := server.NewMetricsBuilder(config.MetricsBackend, &config.MetricsBackendConfig)
137125

138126
downScalerEnabled := config.AutoScale.Down && (config.InKubeCluster || config.KubeConfig != "")
139127
downScalerDelay, err := time.ParseDuration(config.AutoScale.DownAfter)
@@ -147,13 +135,13 @@ func main() {
147135
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
148136

149137
if config.Routes.Config != "" {
150-
err := server.RoutesConfig.ReadRoutesConfig(config.Routes.Config)
138+
err := server.RoutesConfigLoader.Load(config.Routes.Config)
151139
if err != nil {
152140
logrus.WithError(err).Fatal("Unable to load routes from config file")
153141
}
154142

155143
if config.Routes.ConfigWatch {
156-
err := server.RoutesConfig.WatchForChanges(ctx)
144+
err := server.RoutesConfigLoader.WatchForChanges(ctx)
157145
if err != nil {
158146
logrus.WithError(err).Fatal("Unable to watch for changes")
159147
}
@@ -258,7 +246,7 @@ func main() {
258246
case syscall.SIGHUP:
259247
if config.Routes.Config != "" {
260248
logrus.Info("Received SIGHUP, reloading routes config...")
261-
if err := server.RoutesConfig.ReloadRoutesConfig(); err != nil {
249+
if err := server.RoutesConfigLoader.Reload(); err != nil {
262250
logrus.
263251
WithError(err).
264252
WithField("routesConfig", config.Routes.Config).

go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
module github.com/itzg/mc-router
22

3-
go 1.24.0
4-
5-
toolchain go1.24.4
3+
go 1.24.4
64

75
require (
86
github.com/fsnotify/fsnotify v1.9.0

server/api_server.go

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package server
22

33
import (
4+
"encoding/json"
45
"expvar"
56
"net/http"
67

@@ -9,11 +10,12 @@ import (
910
"github.com/sirupsen/logrus"
1011
)
1112

12-
var apiRoutes = mux.NewRouter()
13-
1413
func StartApiServer(apiBinding string) {
1514
logrus.WithField("binding", apiBinding).Info("Serving API requests")
1615

16+
var apiRoutes = mux.NewRouter()
17+
registerApiRoutes(apiRoutes)
18+
1719
apiRoutes.Path("/vars").Handler(expvar.Handler())
1820

1921
apiRoutes.Path("/metrics").Handler(promhttp.Handler())
@@ -23,3 +25,84 @@ func StartApiServer(apiBinding string) {
2325
http.ListenAndServe(apiBinding, apiRoutes)).Error("API server failed")
2426
}()
2527
}
28+
29+
func registerApiRoutes(apiRoutes *mux.Router) {
30+
apiRoutes.Path("/routes").Methods("GET").
31+
HandlerFunc(routesListHandler)
32+
apiRoutes.Path("/routes").Methods("POST").
33+
HandlerFunc(routesCreateHandler)
34+
apiRoutes.Path("/defaultRoute").Methods("POST").
35+
HandlerFunc(routesSetDefault)
36+
apiRoutes.Path("/routes/{serverAddress}").Methods("DELETE").HandlerFunc(routesDeleteHandler)
37+
}
38+
39+
func routesListHandler(writer http.ResponseWriter, _ *http.Request) {
40+
mappings := Routes.GetMappings()
41+
bytes, err := json.Marshal(mappings)
42+
if err != nil {
43+
logrus.WithError(err).Error("Failed to marshal mappings")
44+
writer.WriteHeader(http.StatusInternalServerError)
45+
return
46+
}
47+
48+
writer.Header().Set("Content-Type", "application/json")
49+
_, err = writer.Write(bytes)
50+
if err != nil {
51+
logrus.WithError(err).Error("Failed to write response")
52+
}
53+
}
54+
55+
func routesDeleteHandler(writer http.ResponseWriter, request *http.Request) {
56+
serverAddress := mux.Vars(request)["serverAddress"]
57+
if serverAddress != "" {
58+
if Routes.DeleteMapping(serverAddress) {
59+
writer.WriteHeader(http.StatusOK)
60+
} else {
61+
writer.WriteHeader(http.StatusNotFound)
62+
}
63+
RoutesConfigLoader.SaveRoutes()
64+
}
65+
}
66+
67+
func routesCreateHandler(writer http.ResponseWriter, request *http.Request) {
68+
var definition = struct {
69+
ServerAddress string
70+
Backend string
71+
}{}
72+
73+
//goland:noinspection GoUnhandledErrorResult
74+
defer request.Body.Close()
75+
76+
decoder := json.NewDecoder(request.Body)
77+
err := decoder.Decode(&definition)
78+
if err != nil {
79+
logrus.WithError(err).Error("Unable to get request body")
80+
writer.WriteHeader(http.StatusBadRequest)
81+
return
82+
}
83+
84+
Routes.CreateMapping(definition.ServerAddress, definition.Backend, EmptyScalerFunc, EmptyScalerFunc)
85+
RoutesConfigLoader.SaveRoutes()
86+
writer.WriteHeader(http.StatusCreated)
87+
}
88+
89+
func routesSetDefault(writer http.ResponseWriter, request *http.Request) {
90+
var body = struct {
91+
Backend string
92+
}{}
93+
94+
//goland:noinspection GoUnhandledErrorResult
95+
defer request.Body.Close()
96+
97+
decoder := json.NewDecoder(request.Body)
98+
err := decoder.Decode(&body)
99+
if err != nil {
100+
logrus.WithError(err).Error("Unable to parse request")
101+
writer.WriteHeader(http.StatusBadRequest)
102+
return
103+
}
104+
105+
Routes.SetDefaultRoute(body.Backend)
106+
RoutesConfigLoader.SaveRoutes()
107+
writer.WriteHeader(http.StatusOK)
108+
}
Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package server
22

33
import (
44
"context"
@@ -13,14 +13,13 @@ import (
1313
kitinflux "github.com/go-kit/kit/metrics/influx"
1414
prometheusMetrics "github.com/go-kit/kit/metrics/prometheus"
1515
influx "github.com/influxdata/influxdb1-client/v2"
16-
"github.com/itzg/mc-router/server"
1716
"github.com/prometheus/client_golang/prometheus"
1817
"github.com/prometheus/client_golang/prometheus/promauto"
1918
"github.com/sirupsen/logrus"
2019
)
2120

2221
type MetricsBuilder interface {
23-
BuildConnectorMetrics() *server.ConnectorMetrics
22+
BuildConnectorMetrics() *ConnectorMetrics
2423
Start(ctx context.Context) error
2524
}
2625

@@ -31,6 +30,18 @@ const (
3130
MetricsBackendDiscard = "discard"
3231
)
3332

33+
type MetricsBackendConfig struct {
34+
Influxdb struct {
35+
Interval time.Duration `default:"1m"`
36+
Tags map[string]string `usage:"any extra tags to be included with all reported metrics"`
37+
Addr string
38+
Username string
39+
Password string
40+
Database string
41+
RetentionPolicy string
42+
}
43+
}
44+
3445
// NewMetricsBuilder creates a new MetricsBuilder based on the specified backend.
3546
// If the backend is not recognized, a discard builder is returned.
3647
// config can be nil if the backend is not influxdb.
@@ -57,9 +68,9 @@ func (b expvarMetricsBuilder) Start(ctx context.Context) error {
5768
return nil
5869
}
5970

60-
func (b expvarMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
71+
func (b expvarMetricsBuilder) BuildConnectorMetrics() *ConnectorMetrics {
6172
c := expvarMetrics.NewCounter("connections")
62-
return &server.ConnectorMetrics{
73+
return &ConnectorMetrics{
6374
Errors: expvarMetrics.NewCounter("errors").With("subsystem", "connector"),
6475
BytesTransmitted: expvarMetrics.NewCounter("bytes"),
6576
ConnectionsFrontend: c,
@@ -79,8 +90,8 @@ func (b discardMetricsBuilder) Start(ctx context.Context) error {
7990
return nil
8091
}
8192

82-
func (b discardMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
83-
return &server.ConnectorMetrics{
93+
func (b discardMetricsBuilder) BuildConnectorMetrics() *ConnectorMetrics {
94+
return &ConnectorMetrics{
8495
Errors: discardMetrics.NewCounter(),
8596
BytesTransmitted: discardMetrics.NewCounter(),
8697
ConnectionsFrontend: discardMetrics.NewCounter(),
@@ -121,7 +132,7 @@ func (b *influxMetricsBuilder) Start(ctx context.Context) error {
121132
return nil
122133
}
123134

124-
func (b *influxMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
135+
func (b *influxMetricsBuilder) BuildConnectorMetrics() *ConnectorMetrics {
125136
influxConfig := &b.config.Influxdb
126137

127138
metrics := kitinflux.New(influxConfig.Tags, influx.BatchPointsConfig{
@@ -132,7 +143,7 @@ func (b *influxMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics
132143
b.metrics = metrics
133144

134145
c := metrics.NewCounter("mc_router_connections")
135-
return &server.ConnectorMetrics{
146+
return &ConnectorMetrics{
136147
Errors: metrics.NewCounter("mc_router_errors"),
137148
BytesTransmitted: metrics.NewCounter("mc_router_transmitted_bytes"),
138149
ConnectionsFrontend: c.With("side", "frontend"),
@@ -155,13 +166,13 @@ func (b prometheusMetricsBuilder) Start(ctx context.Context) error {
155166
return nil
156167
}
157168

158-
func (b prometheusMetricsBuilder) BuildConnectorMetrics() *server.ConnectorMetrics {
169+
func (b prometheusMetricsBuilder) BuildConnectorMetrics() *ConnectorMetrics {
159170
pcv = prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
160171
Namespace: "mc_router",
161172
Name: "errors",
162173
Help: "The total number of errors",
163174
}, []string{"type"}))
164-
return &server.ConnectorMetrics{
175+
return &ConnectorMetrics{
165176
Errors: pcv,
166177
BytesTransmitted: prometheusMetrics.NewCounter(promauto.NewCounterVec(prometheus.CounterOpts{
167178
Namespace: "mc_router",

0 commit comments

Comments
 (0)