Skip to content

Commit

Permalink
Allow for MAPPING to be declared with newline delimited entries (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
itzg authored Oct 28, 2023
1 parent ccc0b6f commit 4f54002
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 50 deletions.
37 changes: 29 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,39 @@ The [multi-architecture image published at Docker Hub](https://hub.docker.com/re

## Docker Compose Usage

The following diagram shows how [the example docker-compose.yml](docs/docker-compose.yml)
configures two Minecraft server services named `vanilla` and `forge`, which also become the internal
network aliases. _Notice those services don't need their ports exposed since the internal
networking allows for the inter-container access._
The diagram below shows how this `docker-compose.yml` configures two Minecraft server services named `vanilla` and `forge`, which also become the internal network aliases. _Notice those services don't need their ports exposed since the internal networking allows for the inter-container access._

The `router` service is only one of the services that needs to exposed on the external
network. The `--mapping` declares how the hostname users will enter into their Minecraft client
will map to the internal services.
```yaml
version: "3.8"

services:
vanilla:
image: itzg/minecraft-server
environment:
EULA: "TRUE"
forge:
image: itzg/minecraft-server
environment:
EULA: "TRUE"
TYPE: FORGE
router:
image: ${MC_ROUTER_IMAGE:-itzg/mc-router}
depends_on:
- forge
- vanilla
environment:
MAPPING: |
vanilla.example.com=vanilla:25565
forge.example.com=forge:25565
ports:
- "25565:25565"
```
The `router` service is only one of the services that needs to exposed on the external network. The `MAPPING` declares how the hostname users will enter into their Minecraft client will map to the internal services.

![](docs/compose-diagram.png)

To test out this example, I added these two entries to my "hosts" file:
To test out this example, add these two entries to my "hosts" file:

```
127.0.0.1 vanilla.example.com
Expand Down
49 changes: 17 additions & 32 deletions cmd/mc-router/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os/signal"
"runtime/pprof"
"strconv"
"strings"
"syscall"
"time"

Expand All @@ -30,22 +29,22 @@ type MetricsBackendConfig struct {
}

type Config struct {
Port int `default:"25565" usage:"The [port] bound to listen for Minecraft client connections"`
Default string `usage:"host:port of a default Minecraft server to use when mapping not found"`
Mapping []string `usage:"Comma-separated or repeated mappings of externalHostname=host:port"`
ApiBinding string `usage:"The [host:port] bound for servicing API requests"`
Version bool `usage:"Output version and exit"`
CpuProfile string `usage:"Enables CPU profiling and writes to given path"`
Debug bool `usage:"Enable debug logs"`
ConnectionRateLimit int `default:"1" usage:"Max number of connections to allow per second"`
InKubeCluster bool `usage:"Use in-cluster Kubernetes config"`
KubeConfig string `usage:"The path to a Kubernetes configuration file"`
AutoScaleUp bool `usage:"Increase Kubernetes StatefulSet Replicas (only) from 0 to 1 on respective backend servers when accessed"`
InDockerSwarm bool `usage:"Use in-swarm Docker config"`
DockerTimeout int `default:"0" usage:"Timeout configuration in seconds for the Docker Swarm integration"`
DockerRefreshInterval int `default:"15" usage:"Refresh interval in seconds for the Docker Swarm integration"`
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb"`
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
Port int `default:"25565" usage:"The [port] bound to listen for Minecraft client connections"`
Default string `usage:"host:port of a default Minecraft server to use when mapping not found"`
Mapping map[string]string `usage:"Comma or newline delimited or repeated mappings of externalHostname=host:port"`
ApiBinding string `usage:"The [host:port] bound for servicing API requests"`
Version bool `usage:"Output version and exit"`
CpuProfile string `usage:"Enables CPU profiling and writes to given path"`
Debug bool `usage:"Enable debug logs"`
ConnectionRateLimit int `default:"1" usage:"Max number of connections to allow per second"`
InKubeCluster bool `usage:"Use in-cluster Kubernetes config"`
KubeConfig string `usage:"The path to a Kubernetes configuration file"`
AutoScaleUp bool `usage:"Increase Kubernetes StatefulSet Replicas (only) from 0 to 1 on respective backend servers when accessed"`
InDockerSwarm bool `usage:"Use in-swarm Docker config"`
DockerTimeout int `default:"0" usage:"Timeout configuration in seconds for the Docker Swarm integration"`
DockerRefreshInterval int `default:"15" usage:"Refresh interval in seconds for the Docker Swarm integration"`
MetricsBackend string `default:"discard" usage:"Backend to use for metrics exposure/publishing: discard,expvar,influxdb"`
UseProxyProtocol bool `default:"false" usage:"Send PROXY protocol to backend servers"`
MetricsBackendConfig MetricsBackendConfig
RoutesConfig string `usage:"Name or full path to routes config file"`
NgrokToken string `usage:"If set, an ngrok tunnel will be established. It is HIGHLY recommended to pass as an environment variable."`
Expand Down Expand Up @@ -110,7 +109,7 @@ func main() {
}
}

server.Routes.RegisterAll(parseMappings(config.Mapping))
server.Routes.RegisterAll(config.Mapping)
if config.Default != "" {
server.Routes.SetDefaultRoute(config.Default)
}
Expand Down Expand Up @@ -173,17 +172,3 @@ func main() {
connector.WaitForConnections()
logrus.Info("Stopped")
}

func parseMappings(vals []string) map[string]string {
result := make(map[string]string)
for _, part := range vals {
keyValue := strings.Split(part, "=")
if len(keyValue) == 2 {
result[keyValue[0]] = keyValue[1]
} else {
logrus.WithField("part", part).Fatal("Invalid part of mapping")
}
}

return result
}
11 changes: 7 additions & 4 deletions docs/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3.4'
version: "3.8"

services:
vanilla:
Expand All @@ -18,9 +18,12 @@ services:
environment:
# enable API
API_BINDING: ":25564"
DEBUG: true
MAPPING: |
vanilla.example.com=vanilla:25565
forge.example.com=forge:25565
ports:
- 25565:25565
- "25565:25565"
# bind the API port to only loopback to avoid external exposure
- 127.0.0.1:25564:25564
command: --mapping=vanilla.example.com=vanilla:25565,forge.example.com=forge:25565
- "127.0.0.1:25564:25564"

4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/go-kit/kit v0.13.0
github.com/gorilla/mux v1.8.0
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/itzg/go-flagsfiller v1.12.0
github.com/itzg/go-flagsfiller v1.13.1
github.com/juju/ratelimit v1.0.2
github.com/pires/go-proxyproto v0.7.0
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -59,7 +59,7 @@ require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/iancoleman/strcase v0.2.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/imdario/mergo v0.3.7 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible h1:zaX5fYT98jX5j4UhO/WbfY8T1HkgVrydiDMC9PWqGCo=
Expand All @@ -69,8 +69,8 @@ github.com/inconshreveable/log15/v3 v3.0.0-testing.5 h1:h4e0f3kjgg+RJBlKOabrohjH
github.com/inconshreveable/log15/v3 v3.0.0-testing.5/go.mod h1:3GQg1SVrLoWGfRv/kAZMsdyU5cp8eFc1P3cw+Wwku94=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs=
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/itzg/go-flagsfiller v1.12.0 h1:LSwSUGxzZqueprm0D8FBCAG0JMgwAkkh2UjtwreNgAg=
github.com/itzg/go-flagsfiller v1.12.0/go.mod h1:47WeO9fl+QyS48AdRHfarhF3rKBh0enbe90tTko03gg=
github.com/itzg/go-flagsfiller v1.13.1 h1:MpFWtK12dkz8JwBD3oABoXuxnJ9kdOtXFlW4bC1EpO0=
github.com/itzg/go-flagsfiller v1.13.1/go.mod h1:vSclFjMCgjtH6SB0tCkVyX/OwO/aaInbKmX6H8iJ54Y=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
Expand Down

0 comments on commit 4f54002

Please sign in to comment.