mirror of
https://github.com/itzg/mc-router.git
synced 2024-11-21 11:25:41 +01:00
Add support for a default backend service
This commit is contained in:
parent
d76e5442da
commit
44a67dd359
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,6 @@
|
|||||||
/vendor/
|
/vendor/
|
||||||
|
|
||||||
/.idea/
|
/.idea/
|
||||||
/*.iml
|
/*.iml
|
||||||
|
/mc-router.exe
|
||||||
|
/mc-router
|
||||||
|
16
README.md
16
README.md
@ -25,16 +25,24 @@ Flags:
|
|||||||
"backend": "HOST:PORT"
|
"backend": "HOST:PORT"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* `POST /defaultRoute`
|
||||||
|
Registers a default route to the given backend. JSON body is structured as:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"backend": "HOST:PORT"
|
||||||
|
}
|
||||||
|
```
|
||||||
* `DELETE /routes/{serverAddress}`
|
* `DELETE /routes/{serverAddress}`
|
||||||
Deletes an existing route for the given `serverAddress`
|
Deletes an existing route for the given `serverAddress`
|
||||||
|
|
||||||
## Using kubernetes service auto-discovery
|
## Using kubernetes service auto-discovery
|
||||||
|
|
||||||
When running `mc-router` as a kubernetes pod and you pass the `--in-kube-cluster` command-line argument, then
|
When running `mc-router` as a kubernetes pod and you pass the `--in-kube-cluster` command-line argument, then
|
||||||
it will automatically watch for any services annotated with `mc-router.itzg.me/externalServerName`. The value
|
it will automatically watch for any services annotated with
|
||||||
of the annotation will be registered as the external hostname Minecraft clients would used to connect to the
|
- `mc-router.itzg.me/externalServerName` : The value of the annotation will be registered as the external hostname Minecraft clients would used to connect to the
|
||||||
routed service. The service's clusterIP and target port are used as the routed backend.
|
routed service. The service's clusterIP and target port are used as the routed backend.
|
||||||
|
- `mc-router.itzg.me/defaultServer` : The service's clusterIP and target port are used as the default if
|
||||||
|
no other `externalServiceName` annotations applies.
|
||||||
|
|
||||||
For example, start `mc-router`'s container spec with
|
For example, start `mc-router`'s container spec with
|
||||||
|
|
||||||
|
94
docs/k8s-mc-with-default.yaml
Normal file
94
docs/k8s-mc-with-default.yaml
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mc-stable
|
||||||
|
annotations:
|
||||||
|
"mc-router.itzg.me/defaultServer": "mc.your.domain"
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 25565
|
||||||
|
selector:
|
||||||
|
run: mc-stable
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
run: mc-stable
|
||||||
|
name: mc-stable
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
run: mc-stable
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
run: mc-stable
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
runAsUser: 1000
|
||||||
|
fsGroup: 1000
|
||||||
|
containers:
|
||||||
|
- image: itzg/minecraft-server
|
||||||
|
name: mc-stable
|
||||||
|
env:
|
||||||
|
- name: EULA
|
||||||
|
value: "TRUE"
|
||||||
|
ports:
|
||||||
|
- containerPort: 25565
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mc-stable
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mc-snapshot
|
||||||
|
annotations:
|
||||||
|
"mc-router.itzg.me/externalServerName": "snapshot.your.domain"
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 25565
|
||||||
|
selector:
|
||||||
|
run: mc-snapshot
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
run: mc-snapshot
|
||||||
|
name: mc-snapshot
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
run: mc-snapshot
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
run: mc-snapshot
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
runAsUser: 1000
|
||||||
|
fsGroup: 1000
|
||||||
|
containers:
|
||||||
|
- image: itzg/minecraft-server
|
||||||
|
name: mc-snapshot
|
||||||
|
env:
|
||||||
|
- name: EULA
|
||||||
|
value: "TRUE"
|
||||||
|
- name: VERSION
|
||||||
|
value: "SNAPSHOT"
|
||||||
|
ports:
|
||||||
|
- containerPort: 25565
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: mc-snapshot
|
@ -12,6 +12,11 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AnnotationExternalServerName = "mc-router.itzg.me/externalServerName"
|
||||||
|
AnnotationDefaultServer = "mc-router.itzg.me/defaultServer"
|
||||||
|
)
|
||||||
|
|
||||||
type IK8sWatcher interface {
|
type IK8sWatcher interface {
|
||||||
StartWithConfig(kubeConfigFile string) error
|
StartWithConfig(kubeConfigFile string) error
|
||||||
StartInCluster() error
|
StartInCluster() error
|
||||||
@ -65,7 +70,11 @@ func (w *k8sWatcherImpl) startWithLoadedConfig(config *rest.Config) error {
|
|||||||
if routableService != nil {
|
if routableService != nil {
|
||||||
logrus.WithField("routableService", routableService).Debug("ADD")
|
logrus.WithField("routableService", routableService).Debug("ADD")
|
||||||
|
|
||||||
Routes.CreateMapping(routableService.externalServiceName, routableService.containerEndpoint)
|
if routableService.externalServiceName != "" {
|
||||||
|
Routes.CreateMapping(routableService.externalServiceName, routableService.containerEndpoint)
|
||||||
|
} else {
|
||||||
|
Routes.SetDefaultRoute(routableService.containerEndpoint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DeleteFunc: func(obj interface{}) {
|
DeleteFunc: func(obj interface{}) {
|
||||||
@ -73,7 +82,11 @@ func (w *k8sWatcherImpl) startWithLoadedConfig(config *rest.Config) error {
|
|||||||
if routableService != nil {
|
if routableService != nil {
|
||||||
logrus.WithField("routableService", routableService).Debug("DELETE")
|
logrus.WithField("routableService", routableService).Debug("DELETE")
|
||||||
|
|
||||||
Routes.DeleteMapping(routableService.externalServiceName)
|
if routableService.externalServiceName != "" {
|
||||||
|
Routes.DeleteMapping(routableService.externalServiceName)
|
||||||
|
} else {
|
||||||
|
Routes.SetDefaultRoute("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||||
@ -85,8 +98,12 @@ func (w *k8sWatcherImpl) startWithLoadedConfig(config *rest.Config) error {
|
|||||||
"new": newRoutableService,
|
"new": newRoutableService,
|
||||||
}).Debug("UPDATE")
|
}).Debug("UPDATE")
|
||||||
|
|
||||||
Routes.DeleteMapping(oldRoutableService.externalServiceName)
|
if oldRoutableService.externalServiceName != "" && newRoutableService.externalServiceName != "" {
|
||||||
Routes.CreateMapping(newRoutableService.externalServiceName, newRoutableService.containerEndpoint)
|
Routes.DeleteMapping(oldRoutableService.externalServiceName)
|
||||||
|
Routes.CreateMapping(newRoutableService.externalServiceName, newRoutableService.containerEndpoint)
|
||||||
|
} else {
|
||||||
|
Routes.SetDefaultRoute(newRoutableService.containerEndpoint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -116,22 +133,28 @@ func extractRoutableService(obj interface{}) *routableService {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if externalServiceName, exists := service.Annotations["mc-router.itzg.me/externalServerName"]; exists {
|
if externalServiceName, exists := service.Annotations[AnnotationExternalServerName]; exists {
|
||||||
clusterIp := service.Spec.ClusterIP
|
return buildDetails(service, externalServiceName)
|
||||||
port := "25565"
|
} else if _, exists := service.Annotations[AnnotationDefaultServer]; exists {
|
||||||
for _, p := range service.Spec.Ports {
|
return buildDetails(service, "")
|
||||||
if p.Port == 25565 {
|
|
||||||
if p.TargetPort.String() != "" {
|
|
||||||
port = p.TargetPort.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rs := &routableService{
|
|
||||||
externalServiceName: externalServiceName,
|
|
||||||
containerEndpoint: net.JoinHostPort(clusterIp, port),
|
|
||||||
}
|
|
||||||
return rs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildDetails(service *v1.Service, externalServiceName string) *routableService {
|
||||||
|
clusterIp := service.Spec.ClusterIP
|
||||||
|
port := "25565"
|
||||||
|
for _, p := range service.Spec.Ports {
|
||||||
|
if p.Port == 25565 {
|
||||||
|
if p.TargetPort.String() != "" {
|
||||||
|
port = p.TargetPort.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rs := &routableService{
|
||||||
|
externalServiceName: externalServiceName,
|
||||||
|
containerEndpoint: net.JoinHostPort(clusterIp, port),
|
||||||
|
}
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
"net/http"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -15,6 +15,9 @@ func init() {
|
|||||||
apiRoutes.Path("/routes").Methods("POST").
|
apiRoutes.Path("/routes").Methods("POST").
|
||||||
Headers("Content-Type", "application/json").
|
Headers("Content-Type", "application/json").
|
||||||
HandlerFunc(routesCreateHandler)
|
HandlerFunc(routesCreateHandler)
|
||||||
|
apiRoutes.Path("/defaultRoute").Methods("POST").
|
||||||
|
Headers("Content-Type", "application/json").
|
||||||
|
HandlerFunc(routesSetDefault)
|
||||||
apiRoutes.Path("/routes/{serverAddress}").Methods("DELETE").HandlerFunc(routesDeleteHandler)
|
apiRoutes.Path("/routes/{serverAddress}").Methods("DELETE").HandlerFunc(routesDeleteHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ func routesDeleteHandler(writer http.ResponseWriter, request *http.Request) {
|
|||||||
func routesCreateHandler(writer http.ResponseWriter, request *http.Request) {
|
func routesCreateHandler(writer http.ResponseWriter, request *http.Request) {
|
||||||
var definition = struct {
|
var definition = struct {
|
||||||
ServerAddress string
|
ServerAddress string
|
||||||
Backend string
|
Backend string
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
defer request.Body.Close()
|
defer request.Body.Close()
|
||||||
@ -60,6 +63,25 @@ func routesCreateHandler(writer http.ResponseWriter, request *http.Request) {
|
|||||||
writer.WriteHeader(http.StatusCreated)
|
writer.WriteHeader(http.StatusCreated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func routesSetDefault(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
var body = struct {
|
||||||
|
Backend string
|
||||||
|
}{}
|
||||||
|
|
||||||
|
defer request.Body.Close()
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(request.Body)
|
||||||
|
err := decoder.Decode(&body)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("Unable to parse request")
|
||||||
|
writer.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Routes.SetDefaultRoute(body.Backend)
|
||||||
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
type IRoutes interface {
|
type IRoutes interface {
|
||||||
RegisterAll(mappings map[string]string)
|
RegisterAll(mappings map[string]string)
|
||||||
// FindBackendForServerAddress returns the host:port for the external server address, if registered.
|
// FindBackendForServerAddress returns the host:port for the external server address, if registered.
|
||||||
@ -68,6 +90,7 @@ type IRoutes interface {
|
|||||||
GetMappings() map[string]string
|
GetMappings() map[string]string
|
||||||
DeleteMapping(serverAddress string) bool
|
DeleteMapping(serverAddress string) bool
|
||||||
CreateMapping(serverAddress string, backend string)
|
CreateMapping(serverAddress string, backend string)
|
||||||
|
SetDefaultRoute(backend string)
|
||||||
}
|
}
|
||||||
|
|
||||||
var Routes IRoutes = &routesImpl{}
|
var Routes IRoutes = &routesImpl{}
|
||||||
@ -81,7 +104,16 @@ func (r *routesImpl) RegisterAll(mappings map[string]string) {
|
|||||||
|
|
||||||
type routesImpl struct {
|
type routesImpl struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
mappings map[string]string
|
mappings map[string]string
|
||||||
|
defaultRoute string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *routesImpl) SetDefaultRoute(backend string) {
|
||||||
|
r.defaultRoute = backend
|
||||||
|
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"backend": backend,
|
||||||
|
}).Info("Using default route")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *routesImpl) FindBackendForServerAddress(serverAddress string) string {
|
func (r *routesImpl) FindBackendForServerAddress(serverAddress string) string {
|
||||||
@ -89,9 +121,14 @@ func (r *routesImpl) FindBackendForServerAddress(serverAddress string) string {
|
|||||||
defer r.RUnlock()
|
defer r.RUnlock()
|
||||||
|
|
||||||
if r.mappings == nil {
|
if r.mappings == nil {
|
||||||
return ""
|
return r.defaultRoute
|
||||||
} else {
|
} else {
|
||||||
return r.mappings[serverAddress]
|
|
||||||
|
if route, exists := r.mappings[serverAddress]; exists {
|
||||||
|
return route
|
||||||
|
} else {
|
||||||
|
return r.defaultRoute
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +162,7 @@ func (r *routesImpl) CreateMapping(serverAddress string, backend string) {
|
|||||||
|
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"serverAddress": serverAddress,
|
"serverAddress": serverAddress,
|
||||||
"backend": backend,
|
"backend": backend,
|
||||||
}).Info("Creating route")
|
}).Info("Creating route")
|
||||||
r.mappings[serverAddress] = backend
|
r.mappings[serverAddress] = backend
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user