From 449fa7e3cf9cd04a6fa02be124fc4a88f6c8fd6d Mon Sep 17 00:00:00 2001 From: m1k1o Date: Sun, 7 Mar 2021 22:29:29 +0100 Subject: [PATCH] name + port allocation. --- internal/room/manager.go | 27 ++++++++---- internal/room/ports.go | 92 ++++++++++++++++++++++++++++++++++++++++ internal/types/room.go | 6 ++- 3 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 internal/room/ports.go diff --git a/internal/room/manager.go b/internal/room/manager.go index 526a663..e9f97b8 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -15,6 +15,7 @@ import ( "m1k1o/neko_rooms/internal/config" "m1k1o/neko_rooms/internal/types" + "m1k1o/neko_rooms/internal/utils" ) const ( @@ -63,17 +64,27 @@ func (manager *RoomManagerCtx) List() ([]types.RoomData, error) { return result, nil } func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (*types.RoomData, error) { - // configs - pathName := "foobar" - eprMin := uint(52135) - eprMax := uint(52145) + // TODO: Check if path name exists. + pathName := settings.Name + if pathName == "" { + var err error + pathName, err = utils.NewUID(32) + if err != nil { + return nil, err + } + } + + epr, err := manager.allocatePorts(settings.MaxConnections) + if err != nil { + return nil, err + } portBindings := nat.PortMap{} exposedPorts := nat.PortSet{ nat.Port(fmt.Sprintf("%d/udp", frontendPort)): struct{}{}, } - for port := eprMin; port <= eprMax; port++ { + for port := epr.Min; port <= epr.Max; port++ { portKey := nat.Port(fmt.Sprintf("%d/udp", port)) portBindings[portKey] = []nat.PortBinding{ @@ -91,8 +102,8 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (*types.RoomD labels := map[string]string{ // Set internal labels "m1k1o.neko_rooms.canary": labelCanary, - "m1k1o.neko_rooms.epr.min": fmt.Sprintf("%d", eprMin), - "m1k1o.neko_rooms.epr.max": fmt.Sprintf("%d", eprMax), + "m1k1o.neko_rooms.epr.min": fmt.Sprintf("%d", epr.Min), + "m1k1o.neko_rooms.epr.max": fmt.Sprintf("%d", epr.Max), // Set traefik labels "traefik.enable": "true", @@ -121,7 +132,7 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (*types.RoomD // List of environment variable to set in the container Env: append([]string{ fmt.Sprintf("NEKO_BIND=%d", frontendPort), - }, settings.Env(eprMin, eprMax, manager.config.NAT1To1IPs)...), + }, settings.Env(epr.Min, epr.Max, manager.config.NAT1To1IPs)...), // Name of the image as it was passed by the operator (e.g. could be symbolic) Image: nekoImage, // List of labels set to this container diff --git a/internal/room/ports.go b/internal/room/ports.go new file mode 100644 index 0000000..51c8726 --- /dev/null +++ b/internal/room/ports.go @@ -0,0 +1,92 @@ +package room + +import ( + "fmt" + "sort" + "strconv" +) + +type EprPorts struct { + Min uint16 + Max uint16 +} + +func (manager *RoomManagerCtx) allocatePorts(sum uint16) (EprPorts, error) { + min := manager.config.EprMin + max := manager.config.EprMax + + epr := EprPorts{ + Min: min, + Max: min + sum, + } + + ports, err := manager.getUsedPorts() + if err != nil { + return epr, err + } + + for _, port := range ports { + if (epr.Min >= port.Min && epr.Min <= port.Max) || (epr.Max >= port.Min && epr.Max <= port.Max) { + epr.Min = port.Max + 1 + epr.Max = port.Max + 1 + sum + } + } + + if epr.Min > max || epr.Max > max { + return epr, fmt.Errorf("Unable to allocate ports: not enough ports.") + } + + return epr, nil +} + +func (manager *RoomManagerCtx) getUsedPorts() ([]EprPorts, error) { + containers, err := manager.listContainers() + if err != nil { + return nil, err + } + + result := []EprPorts{} + for _, container := range containers { + epr, err := manager.getEprFromLabels(container.Labels) + if err != nil { + return nil, err + } + + result = append(result, epr) + } + + sort.SliceStable(result, func(i, j int) bool { + return result[i].Min < result[j].Min + }) + + return result, nil +} + +func (manager *RoomManagerCtx) getEprFromLabels(labels map[string]string) (EprPorts, error) { + var err error + epr := EprPorts{} + + eprMinStr, ok := labels["m1k1o.neko_rooms.epr.min"] + if !ok { + return epr, fmt.Errorf("Damaged container labels: epr.min not found.") + } + + eprMin, err := strconv.ParseUint(eprMinStr, 10, 16) + if err != nil { + return epr, err + } + + eprMaxStr, ok := labels["m1k1o.neko_rooms.epr.max"] + if !ok { + return epr, fmt.Errorf("Damaged container labels: epr.max not found.") + } + + eprMax, err := strconv.ParseUint(eprMaxStr, 10, 16) + if err != nil { + return epr, err + } + + epr.Min = uint16(eprMin) + epr.Max = uint16(eprMax) + return epr, nil +} diff --git a/internal/types/room.go b/internal/types/room.go index 64b0c17..8eab46b 100644 --- a/internal/types/room.go +++ b/internal/types/room.go @@ -6,7 +6,9 @@ import ( ) type RoomSettings struct { - MaxConnections uint `json:"max_connections"` + Name string `json:"name"` + + MaxConnections uint16 `json:"max_connections"` UserPass string `json:"user_pass"` AdminPass string `json:"admin_pass"` @@ -23,7 +25,7 @@ type RoomSettings struct { AudioPipeline string `json:"audio_pipeline"` } -func (settings *RoomSettings) Env(eprMin uint, eprMax uint, nat1to1 []string) []string { +func (settings *RoomSettings) Env(eprMin uint16, eprMax uint16, nat1to1 []string) []string { env := []string{ fmt.Sprintf("NEKO_EPR=%d-%d", eprMin, eprMax), fmt.Sprintf("NEKO_NAT1TO1=%s", strings.Join(nat1to1, ",")),