add labels filter support to API.

This commit is contained in:
Miroslav Šedivý
2023-09-30 17:26:44 +02:00
parent 7b38250698
commit e7b9d39a7c
10 changed files with 136 additions and 59 deletions
+15 -1
View File
@@ -3,14 +3,28 @@ package api
import (
"encoding/json"
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/m1k1o/neko-rooms/internal/room"
"github.com/m1k1o/neko-rooms/internal/types"
)
func (manager *ApiManagerCtx) roomsList(w http.ResponseWriter, r *http.Request) {
response, err := manager.rooms.List()
labelsMap := map[string]string{}
for key, value := range r.URL.Query() {
key = strings.ToLower(key)
if !room.CheckLabelKey(key) {
http.Error(w, "invalid label name, allowed characters: [a-z0-9.-]", 400)
return
}
labelsMap[key] = value[0]
}
response, err := manager.rooms.List(labelsMap)
if err != nil {
http.Error(w, err.Error(), 500)
return
+9 -26
View File
@@ -28,6 +28,7 @@ func (manager *RoomManagerCtx) containerToEntry(container dockerTypes.Container)
Running: container.State == "running",
Status: container.Status,
Created: time.Unix(container.Created, 0),
Labels: labels.UserDefined,
}
if labels.Mux {
@@ -37,35 +38,23 @@ func (manager *RoomManagerCtx) containerToEntry(container dockerTypes.Container)
return entry, nil
}
func (manager *RoomManagerCtx) listContainers() ([]dockerTypes.Container, error) {
func (manager *RoomManagerCtx) listContainers(labels map[string]string) ([]dockerTypes.Container, error) {
args := filters.NewArgs(
filters.Arg("label", "m1k1o.neko_rooms.instance"),
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", manager.config.InstanceName)),
)
containers, err := manager.client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{
for key, val := range labels {
args.Add("label", fmt.Sprintf("m1k1o.neko_rooms.x-%s=%s", key, val))
}
return manager.client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{
All: true,
Filters: args,
})
if err != nil {
return nil, err
}
result := []dockerTypes.Container{}
for _, container := range containers {
val, ok := container.Labels["m1k1o.neko_rooms.instance"]
if !ok || val != manager.config.InstanceName {
continue
}
result = append(result, container)
}
return result, nil
}
func (manager *RoomManagerCtx) containerFilter(args filters.Args) (*dockerTypes.Container, error) {
args.Add("label", "m1k1o.neko_rooms.instance")
args.Add("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", manager.config.InstanceName))
containers, err := manager.client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{
All: true,
@@ -81,12 +70,6 @@ func (manager *RoomManagerCtx) containerFilter(args filters.Args) (*dockerTypes.
}
container := containers[0]
val, ok := container.Labels["m1k1o.neko_rooms.instance"]
if !ok || val != manager.config.InstanceName {
return nil, fmt.Errorf("this container does not belong to neko_rooms")
}
return &container, nil
}
+25
View File
@@ -2,11 +2,15 @@ package room
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/m1k1o/neko-rooms/internal/types"
)
var labelRegex = regexp.MustCompile(`^[a-z0-9.-]+$`)
type RoomLabels struct {
Name string
URL string
@@ -15,6 +19,7 @@ type RoomLabels struct {
NekoImage string
BrowserPolicy *BrowserPolicyLabels
UserDefined map[string]string
}
type BrowserPolicyLabels struct {
@@ -101,6 +106,14 @@ func (manager *RoomManagerCtx) extractLabels(labels map[string]string) (*RoomLab
}
}
// extract user defined labels
userDefined := map[string]string{}
for key, val := range labels {
if strings.HasPrefix(key, "m1k1o.neko_rooms.x-") {
userDefined[strings.TrimPrefix(key, "m1k1o.neko_rooms.x-")] = val
}
}
return &RoomLabels{
Name: name,
URL: url,
@@ -109,6 +122,7 @@ func (manager *RoomManagerCtx) extractLabels(labels map[string]string) (*RoomLab
Epr: epr,
BrowserPolicy: browserPolicy,
UserDefined: userDefined,
}, nil
}
@@ -133,5 +147,16 @@ func (manager *RoomManagerCtx) serializeLabels(labels RoomLabels) map[string]str
labelsMap["m1k1o.neko_rooms.browser_policy.path"] = labels.BrowserPolicy.Path
}
for key, val := range labels.UserDefined {
// to lowercase
key = strings.ToLower(key)
labelsMap[fmt.Sprintf("m1k1o.neko_rooms.x-%s", key)] = val
}
return labelsMap
}
func CheckLabelKey(name string) bool {
return labelRegex.MatchString(name)
}
+8 -5
View File
@@ -6,6 +6,7 @@ import (
"encoding/csv"
"encoding/json"
"fmt"
"net/url"
"os"
"path"
"path/filepath"
@@ -62,13 +63,13 @@ func (manager *RoomManagerCtx) Config() types.RoomsConfig {
}
}
func (manager *RoomManagerCtx) List() ([]types.RoomEntry, error) {
containers, err := manager.listContainers()
func (manager *RoomManagerCtx) List(labels map[string]string) ([]types.RoomEntry, error) {
containers, err := manager.listContainers(labels)
if err != nil {
return nil, err
}
result := []types.RoomEntry{}
result := make([]types.RoomEntry, 0, len(containers))
for _, container := range containers {
entry, err := manager.containerToEntry(container)
if err != nil {
@@ -174,6 +175,7 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro
NekoImage: settings.NekoImage,
BrowserPolicy: browserPolicyLabels,
UserDefined: settings.Labels,
})
//
@@ -652,9 +654,10 @@ func (manager *RoomManagerCtx) GetSettings(id string) (*types.RoomSettings, erro
Name: labels.Name,
NekoImage: labels.NekoImage,
MaxConnections: labels.Epr.Max - labels.Epr.Min + 1,
Labels: labels.UserDefined,
Mounts: mounts,
BrowserPolicy: browserPolicy,
Resources: roomResources,
BrowserPolicy: browserPolicy,
}
if labels.Mux {
@@ -678,7 +681,7 @@ func (manager *RoomManagerCtx) GetStats(id string) (*types.RoomStats, error) {
}
output, err := manager.containerExec(id, []string{
"wget", "-q", "-O-", "http://127.0.0.1:8080/stats?pwd=" + settings.AdminPass,
"wget", "-q", "-O-", "http://127.0.0.1:8080/stats?pwd=" + url.QueryEscape(settings.AdminPass),
})
if err != nil {
return nil, err
+1 -1
View File
@@ -44,7 +44,7 @@ func (manager *RoomManagerCtx) allocatePorts(sum uint16) (EprPorts, error) {
}
func (manager *RoomManagerCtx) getUsedPorts() ([]EprPorts, error) {
containers, err := manager.listContainers()
containers, err := manager.listContainers(nil)
if err != nil {
return nil, err
}
+12 -10
View File
@@ -35,15 +35,16 @@ type RoomsConfig struct {
}
type RoomEntry struct {
ID string `json:"id"`
URL string `json:"url"`
Name string `json:"name"`
NekoImage string `json:"neko_image"`
IsOutdated bool `json:"is_outdated"`
MaxConnections uint16 `json:"max_connections"` // 0 when using mux
Running bool `json:"running"`
Status string `json:"status"`
Created time.Time `json:"created"`
ID string `json:"id"`
URL string `json:"url"`
Name string `json:"name"`
NekoImage string `json:"neko_image"`
IsOutdated bool `json:"is_outdated"`
MaxConnections uint16 `json:"max_connections"` // 0 when using mux
Running bool `json:"running"`
Status string `json:"status"`
Created time.Time `json:"created"`
Labels map[string]string `json:"labels,omitempty"`
}
type MountType string
@@ -94,6 +95,7 @@ type RoomSettings struct {
BroadcastPipeline string `json:"broadcast_pipeline,omitempty"`
Envs map[string]string `json:"envs"`
Labels map[string]string `json:"labels"`
Mounts []RoomMount `json:"mounts"`
Resources RoomResources `json:"resources"`
@@ -280,7 +282,7 @@ type RoomMember struct {
type RoomManager interface {
Config() RoomsConfig
List() ([]RoomEntry, error)
List(labels map[string]string) ([]RoomEntry, error)
FindByName(name string) (*RoomEntry, error)
Create(settings RoomSettings) (string, error)