feat(k8s): ExternalName service support (#419)
This commit is contained in:
@@ -220,8 +220,8 @@ For more information on the allow/deny list configuration, see the [json schema]
|
||||
### Using Kubernetes Service auto-discovery
|
||||
|
||||
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 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. You can use more hostnames by splitting them with comma.
|
||||
- `mc-router.itzg.me/defaultServer` : The service's clusterIP and target port are used as the default if no other `externalServiceName` annotations applies.
|
||||
- `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 is used as the routed backend. You can use more hostnames by splitting them with comma.
|
||||
- `mc-router.itzg.me/defaultServer` : The service is used as the default if no other `externalServiceName` annotations applies.
|
||||
|
||||
For example, start `mc-router`'s container spec with
|
||||
|
||||
@@ -253,7 +253,11 @@ metadata:
|
||||
"mc-router.itzg.me/externalServerName": "external.host.name,other.host.name"
|
||||
```
|
||||
|
||||
mc-router will pick the service port named either `minecraft` or `mc-router`. If neither port names exist, it will use port value 25565.
|
||||
### Service parsing
|
||||
|
||||
To detrmine the endpoint mc-router will pick the host from `spec.clusterIP` by default, if the service is of type `ExtenalName` it will use `spec.externalName` instead.
|
||||
|
||||
For the port it will look in `spec.ports` for a port named `mc-router`, if not present `minecraft` or, if neither port names exist, it will use default minecraft port value 25565.
|
||||
|
||||
### Example Kubernetes deployment
|
||||
|
||||
|
||||
+16
-3
@@ -235,11 +235,24 @@ func (w *k8sWatcherImpl) extractRoutableServices(obj interface{}) []*routableSer
|
||||
|
||||
func (w *k8sWatcherImpl) buildDetails(service *core.Service, externalServiceName string) *routableService {
|
||||
clusterIp := service.Spec.ClusterIP
|
||||
port := "25565"
|
||||
if service.Spec.Type == core.ServiceTypeExternalName {
|
||||
clusterIp = service.Spec.ExternalName
|
||||
}
|
||||
mcRouterPort := ""
|
||||
mcPort := ""
|
||||
for _, p := range service.Spec.Ports {
|
||||
if p.Name == "mc-router" || p.Name == "minecraft" {
|
||||
port = strconv.Itoa(int(p.Port))
|
||||
if p.Name == "mc-router" {
|
||||
mcRouterPort = strconv.Itoa(int(p.Port))
|
||||
}
|
||||
if p.Name == "minecraft" {
|
||||
mcPort = strconv.Itoa(int(p.Port))
|
||||
}
|
||||
}
|
||||
port := "25565"
|
||||
if len(mcRouterPort) > 0 {
|
||||
port = mcRouterPort
|
||||
} else if len(mcPort) > 0 {
|
||||
port = mcPort
|
||||
}
|
||||
rs := &routableService{
|
||||
externalServiceName: externalServiceName,
|
||||
|
||||
+98
-2
@@ -80,7 +80,7 @@ func TestK8sWatcherImpl_handleAddThenUpdate(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// DownScaler needs to be instantiated
|
||||
DownScaler = NewDownScaler(context.Background(), false, 1 * time.Second)
|
||||
DownScaler = NewDownScaler(context.Background(), false, 1*time.Second)
|
||||
Routes.Reset()
|
||||
|
||||
watcher := &k8sWatcherImpl{}
|
||||
@@ -153,7 +153,7 @@ func TestK8sWatcherImpl_handleAddThenDelete(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// DownScaler needs to be instantiated
|
||||
DownScaler = NewDownScaler(context.Background(), false, 1 * time.Second)
|
||||
DownScaler = NewDownScaler(context.Background(), false, 1*time.Second)
|
||||
Routes.Reset()
|
||||
|
||||
watcher := &k8sWatcherImpl{}
|
||||
@@ -175,3 +175,99 @@ func TestK8sWatcherImpl_handleAddThenDelete(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestK8s_externalName(t *testing.T) {
|
||||
type scenario struct {
|
||||
given string
|
||||
expect string
|
||||
}
|
||||
type svcAndScenarios struct {
|
||||
svc string
|
||||
scenarios []scenario
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
initial svcAndScenarios
|
||||
update svcAndScenarios
|
||||
}{
|
||||
{
|
||||
name: "typeChange",
|
||||
initial: svcAndScenarios{
|
||||
svc: ` {"metadata": {"annotations": {"mc-router.itzg.me/externalServerName": "a.com"}}, "spec":{"type":"ExternalName", "externalName": "mc-server.com"}}`,
|
||||
scenarios: []scenario{
|
||||
{given: "a.com", expect: "mc-server.com:25565"},
|
||||
{given: "b.com", expect: ""},
|
||||
},
|
||||
},
|
||||
update: svcAndScenarios{
|
||||
svc: ` {"metadata": {"annotations": {"mc-router.itzg.me/externalServerName": "a.com"}}, "spec":{"clusterIP": "1.1.1.1"}}`,
|
||||
scenarios: []scenario{
|
||||
{given: "a.com", expect: "1.1.1.1:25565"},
|
||||
{given: "b.com", expect: ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "typeAndServerChange",
|
||||
initial: svcAndScenarios{
|
||||
svc: ` {"metadata": {"annotations": {"mc-router.itzg.me/externalServerName": "a.com"}}, "spec":{"type":"ExternalName", "externalName": "mc-server.com"}}`,
|
||||
scenarios: []scenario{
|
||||
{given: "a.com", expect: "mc-server.com:25565"},
|
||||
{given: "b.com", expect: ""},
|
||||
},
|
||||
},
|
||||
update: svcAndScenarios{
|
||||
svc: ` {"metadata": {"annotations": {"mc-router.itzg.me/externalServerName": "b.com"}}, "spec":{"clusterIP": "1.1.1.1"}}`,
|
||||
scenarios: []scenario{
|
||||
{given: "a.com", expect: ""},
|
||||
{given: "b.com", expect: "1.1.1.1:25565"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "externalNameChange",
|
||||
initial: svcAndScenarios{
|
||||
svc: ` {"metadata": {"annotations": {"mc-router.itzg.me/externalServerName": "a.com,b.com"}}, "spec":{"type":"ExternalName", "externalName": "mc-server.com"}}`,
|
||||
scenarios: []scenario{
|
||||
{given: "a.com", expect: "mc-server.com:25565"},
|
||||
{given: "b.com", expect: "mc-server.com:25565"},
|
||||
},
|
||||
},
|
||||
update: svcAndScenarios{
|
||||
svc: ` {"metadata": {"annotations": {"mc-router.itzg.me/externalServerName": "a.com,b.com"}}, "spec":{"type":"ExternalName", "externalName": "1.1.1.1"}}`,
|
||||
scenarios: []scenario{
|
||||
{given: "a.com", expect: "1.1.1.1:25565"},
|
||||
{given: "b.com", expect: "1.1.1.1:25565"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// DownScaler needs to be instantiated
|
||||
DownScaler = NewDownScaler(context.Background(), false, 1*time.Second)
|
||||
Routes.Reset()
|
||||
|
||||
watcher := &k8sWatcherImpl{}
|
||||
initialSvc := v1.Service{}
|
||||
err := json.Unmarshal([]byte(test.initial.svc), &initialSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
watcher.handleAdd(&initialSvc)
|
||||
for _, s := range test.initial.scenarios {
|
||||
backend, _, _, _ := Routes.FindBackendForServerAddress(context.Background(), s.given)
|
||||
assert.Equal(t, s.expect, backend, "initial: given=%s", s.given)
|
||||
}
|
||||
|
||||
updatedSvc := v1.Service{}
|
||||
err = json.Unmarshal([]byte(test.update.svc), &updatedSvc)
|
||||
require.NoError(t, err)
|
||||
|
||||
watcher.handleUpdate(&initialSvc, &updatedSvc)
|
||||
for _, s := range test.update.scenarios {
|
||||
backend, _, _, _ := Routes.FindBackendForServerAddress(context.Background(), s.given)
|
||||
assert.Equal(t, s.expect, backend, "update: given=%s", s.given)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user