Enable network filtering for docker swarm discovery (#143)

This commit is contained in:
n1xx1 2022-11-17 15:38:44 +01:00 committed by GitHub
parent 298ea8eef9
commit c2b23d40c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 15 deletions

View File

@ -206,6 +206,8 @@ label on the service. These are the labels scanned:
The default value is 25565. The default value is 25565.
- `mc-router.default`: Set this to a truthy value to make this server the deafult backend. - `mc-router.default`: Set this to a truthy value to make this server the deafult backend.
Please note that `mc-router.host` is still required to be set. Please note that `mc-router.host` is still required to be set.
- `mc-router.network`: Specify the network you are using for the router if multiple are
present in the service. You can either use the network ID, it's full name or an alias.
## Example Docker Swarm deployment ## Example Docker Swarm deployment

View File

@ -23,6 +23,7 @@ services:
replicas: 1 replicas: 1
labels: labels:
- "mc-router.host=foo.host.name,bar.host.name" - "mc-router.host=foo.host.name,bar.host.name"
- "mc-router.network=minecraft" # not required in this case
volumes: volumes:
- mcfoobardata:/data - mcfoobardata:/data
networks: networks:
@ -39,6 +40,9 @@ services:
replicas: 1 replicas: 1
labels: labels:
- "mc-router.host=baz.host.name" - "mc-router.host=baz.host.name"
- "mc-router.network=minecraft" # required since we are exposing a port
ports:
- "25575:25575" # RCON
volumes: volumes:
- mcbazdata:/data - mcbazdata:/data
networks: networks:

View File

@ -37,6 +37,7 @@ const (
DockerRouterLabelHost = "mc-router.host" DockerRouterLabelHost = "mc-router.host"
DockerRouterLabelPort = "mc-router.port" DockerRouterLabelPort = "mc-router.port"
DockerRouterLabelDefault = "mc-router.default" DockerRouterLabelDefault = "mc-router.default"
DockerRouterLabelNetwork = "mc-router.network"
) )
func (w *dockerWatcherImpl) makeWakerFunc(service *routableService) func(ctx context.Context) error { func (w *dockerWatcherImpl) makeWakerFunc(service *routableService) func(ctx context.Context) error {
@ -179,7 +180,7 @@ func (w *dockerWatcherImpl) listServices(ctx context.Context) ([]*routableServic
continue continue
} }
data, ok := w.parseServiceData(&service) data, ok := w.parseServiceData(&service, networkMap)
if !ok { if !ok {
continue continue
} }
@ -201,21 +202,46 @@ func (w *dockerWatcherImpl) listServices(ctx context.Context) ([]*routableServic
return result, nil return result, nil
} }
func dockerCheckNetworkName(id string, name string, networkMap map[string]*dockertypes.NetworkResource, networkAliases map[string][]string) (bool, error) {
// we allow to specify the id instead
if id == name {
return true, nil
}
if network := networkMap[id]; network != nil {
if network.Name == name {
return true, nil
}
aliases := networkAliases[id]
for _, alias := range aliases {
if alias == name {
return true, nil
}
}
return false, nil
}
return false, fmt.Errorf("network not found %s", id)
}
type parsedDockerServiceData struct { type parsedDockerServiceData struct {
hosts []string hosts []string
port uint64 port uint64
def *bool def *bool
network *string
ip string ip string
} }
func (w *dockerWatcherImpl) parseServiceData(service *swarm.Service) (data parsedDockerServiceData, ok bool) { func (w *dockerWatcherImpl) parseServiceData(service *swarm.Service, networkMap map[string]*dockertypes.NetworkResource) (data parsedDockerServiceData, ok bool) {
ok = true networkAliases := map[string][]string{}
for _, network := range service.Spec.TaskTemplate.Networks {
networkAliases[network.Target] = network.Aliases
}
for key, value := range service.Spec.Labels { for key, value := range service.Spec.Labels {
if key == DockerRouterLabelHost { if key == DockerRouterLabelHost {
if data.hosts != nil { if data.hosts != nil {
logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}). logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
Warnf("ignoring service with duplicate %s", DockerRouterLabelHost) Warnf("ignoring service with duplicate %s", DockerRouterLabelHost)
ok = false
return return
} }
data.hosts = strings.Split(value, ",") data.hosts = strings.Split(value, ",")
@ -224,7 +250,6 @@ func (w *dockerWatcherImpl) parseServiceData(service *swarm.Service) (data parse
if data.port != 0 { if data.port != 0 {
logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}). logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
Warnf("ignoring service with duplicate %s", DockerRouterLabelPort) Warnf("ignoring service with duplicate %s", DockerRouterLabelPort)
ok = false
return return
} }
var err error var err error
@ -233,7 +258,6 @@ func (w *dockerWatcherImpl) parseServiceData(service *swarm.Service) (data parse
logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}). logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
WithError(err). WithError(err).
Warnf("ignoring service with invalid %s", DockerRouterLabelPort) Warnf("ignoring service with invalid %s", DockerRouterLabelPort)
ok = false
return return
} }
} }
@ -241,7 +265,6 @@ func (w *dockerWatcherImpl) parseServiceData(service *swarm.Service) (data parse
if data.def != nil { if data.def != nil {
logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}). logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
Warnf("ignoring service with duplicate %s", DockerRouterLabelDefault) Warnf("ignoring service with duplicate %s", DockerRouterLabelDefault)
ok = false
return return
} }
data.def = new(bool) data.def = new(bool)
@ -249,19 +272,58 @@ func (w *dockerWatcherImpl) parseServiceData(service *swarm.Service) (data parse
lowerValue := strings.TrimSpace(strings.ToLower(value)) lowerValue := strings.TrimSpace(strings.ToLower(value))
*data.def = lowerValue != "" && lowerValue != "0" && lowerValue != "false" && lowerValue != "no" *data.def = lowerValue != "" && lowerValue != "0" && lowerValue != "false" && lowerValue != "no"
} }
} if key == DockerRouterLabelNetwork {
if len(data.hosts) == 0 { if data.network != nil {
ok = false logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
Warnf("ignoring service with duplicate %s", DockerRouterLabelNetwork)
return return
} }
data.network = new(string)
*data.network = value
}
}
// probably not minecraft related
if len(data.hosts) == 0 {
return
}
if len(service.Endpoint.VirtualIPs) == 0 {
logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
Warnf("ignoring service, no VirtualIPs found")
return
}
if data.port == 0 { if data.port == 0 {
data.port = 25565 data.port = 25565
} }
virtualIP := service.Endpoint.VirtualIPs[0] vipIndex := -1
if data.network != nil {
for i, vip := range service.Endpoint.VirtualIPs {
if ok, err := dockerCheckNetworkName(vip.NetworkID, *data.network, networkMap, networkAliases); ok {
vipIndex = i
break
} else if err != nil {
// we intentionally ignore name check errors
logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
Debugf("%v", err)
}
}
if vipIndex == -1 {
logrus.WithFields(logrus.Fields{"serviceId": service.ID, "serviceName": service.Spec.Name}).
Warnf("ignoring service, network %s not found", *data.network)
return
}
} else {
// if network isn't specified assume it's the first one
vipIndex = 0
}
virtualIP := service.Endpoint.VirtualIPs[vipIndex]
ip, _, _ := net.ParseCIDR(virtualIP.Addr) ip, _, _ := net.ParseCIDR(virtualIP.Addr)
data.ip = ip.String() data.ip = ip.String()
ok = true
return return
} }