mirror of
https://github.com/m1k1o/neko-rooms.git
synced 2026-11-02 22:51:34 -06:00
proxy store context in labels.
This commit is contained in:
+35
-65
@@ -6,8 +6,6 @@ import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
@@ -15,8 +13,6 @@ import (
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/m1k1o/neko-rooms/internal/config"
|
||||
)
|
||||
|
||||
type ProxyManagerCtx struct {
|
||||
@@ -24,26 +20,19 @@ type ProxyManagerCtx struct {
|
||||
mu sync.RWMutex
|
||||
ctx context.Context
|
||||
cancel func()
|
||||
prefix string
|
||||
|
||||
client *dockerClient.Client
|
||||
config *config.Room
|
||||
handlers map[string]*httputil.ReverseProxy
|
||||
}
|
||||
|
||||
func New(client *dockerClient.Client, config *config.Room) *ProxyManagerCtx {
|
||||
logger := log.With().Str("module", "proxy").Logger()
|
||||
|
||||
prefix := config.PathPrefix
|
||||
if !strings.HasSuffix(prefix, "/") {
|
||||
prefix += "/"
|
||||
instanceName string
|
||||
handlers *prefixHandler
|
||||
}
|
||||
|
||||
func New(client *dockerClient.Client, instanceName string) *ProxyManagerCtx {
|
||||
return &ProxyManagerCtx{
|
||||
logger: logger,
|
||||
prefix: prefix,
|
||||
logger: log.With().Str("module", "proxy").Logger(),
|
||||
|
||||
client: client,
|
||||
config: config,
|
||||
handlers: map[string]*httputil.ReverseProxy{},
|
||||
instanceName: instanceName,
|
||||
handlers: &prefixHandler{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +48,8 @@ func (p *ProxyManagerCtx) Start() {
|
||||
msgs, errs := p.client.Events(p.ctx, dockerTypes.EventsOptions{
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("type", "container"),
|
||||
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.config.InstanceName)),
|
||||
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.instanceName)),
|
||||
filters.Arg("label", "m1k1o.neko_rooms.proxy=true"),
|
||||
filters.Arg("event", "create"),
|
||||
filters.Arg("event", "start"),
|
||||
//filters.Arg("event", "health_status"),
|
||||
@@ -73,30 +63,32 @@ func (p *ProxyManagerCtx) Start() {
|
||||
case err := <-errs:
|
||||
p.logger.Info().Interface("err", err).Msg("eee")
|
||||
case msg := <-msgs:
|
||||
name, host, ok := p.parseLabels(msg.Actor.Attributes)
|
||||
path, port, ok := p.parseLabels(msg.Actor.Attributes)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
p.logger.Info().
|
||||
Str("action", msg.Action).
|
||||
Str("name", name).
|
||||
Str("host", host).
|
||||
Str("path", path).
|
||||
Str("port", port).
|
||||
Msg("new docker event")
|
||||
|
||||
p.mu.Lock()
|
||||
switch msg.Action {
|
||||
case "create":
|
||||
p.handlers[name] = nil
|
||||
p.handlers.Set(path, nil)
|
||||
case "start":
|
||||
p.handlers[name] = httputil.NewSingleHostReverseProxy(&url.URL{
|
||||
proxy := httputil.NewSingleHostReverseProxy(&url.URL{
|
||||
Scheme: "http",
|
||||
Host: host,
|
||||
Host: msg.ID[:12] + ":" + port,
|
||||
})
|
||||
|
||||
p.handlers.Set(path, proxy)
|
||||
case "stop":
|
||||
p.handlers[name] = nil
|
||||
p.handlers.Set(path, nil)
|
||||
case "destroy":
|
||||
delete(p.handlers, name)
|
||||
p.handlers.Remove(path)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
}
|
||||
@@ -116,7 +108,7 @@ func (p *ProxyManagerCtx) Refresh() error {
|
||||
containers, err := p.client.ContainerList(p.ctx, dockerTypes.ContainerListOptions{
|
||||
All: true,
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.config.InstanceName)),
|
||||
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.instanceName)),
|
||||
),
|
||||
})
|
||||
|
||||
@@ -124,63 +116,42 @@ func (p *ProxyManagerCtx) Refresh() error {
|
||||
return err
|
||||
}
|
||||
|
||||
p.handlers = map[string]*httputil.ReverseProxy{}
|
||||
p.handlers = &prefixHandler{}
|
||||
|
||||
for _, cont := range containers {
|
||||
name, host, ok := p.parseLabels(cont.Labels)
|
||||
path, port, ok := p.parseLabels(cont.Labels)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
var proxy *httputil.ReverseProxy
|
||||
if cont.State == "running" {
|
||||
p.handlers[name] = httputil.NewSingleHostReverseProxy(&url.URL{
|
||||
proxy = httputil.NewSingleHostReverseProxy(&url.URL{
|
||||
Scheme: "http",
|
||||
Host: host,
|
||||
Host: cont.ID[:12] + ":" + port,
|
||||
})
|
||||
} else {
|
||||
p.handlers[name] = nil
|
||||
}
|
||||
|
||||
p.handlers.Set(path, proxy)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProxyManagerCtx) parseLabels(labels map[string]string) (name string, host string, ok bool) {
|
||||
name, ok = labels["m1k1o.neko_rooms.name"]
|
||||
func (p *ProxyManagerCtx) parseLabels(labels map[string]string) (path string, port string, ok bool) {
|
||||
path, ok = labels["m1k1o.neko_rooms.proxy.path"]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
host, ok = labels["m1k1o.neko_rooms.host"]
|
||||
port, ok = labels["m1k1o.neko_rooms.proxy.port"]
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ProxyManagerCtx) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// only if has prefix
|
||||
if !strings.HasPrefix(r.URL.Path, p.prefix) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// remove prefix and leading /
|
||||
roomPath := strings.TrimPrefix(r.URL.Path, p.prefix)
|
||||
roomPath = strings.TrimLeft(roomPath, "/")
|
||||
if roomPath == "" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// get room name
|
||||
roomName, doRedir := roomPath, false
|
||||
if i := strings.Index(roomPath, "/"); i != -1 {
|
||||
roomName = roomPath[:i]
|
||||
} else {
|
||||
doRedir = true
|
||||
}
|
||||
|
||||
// get proxy by room name
|
||||
p.mu.RLock()
|
||||
proxy, ok := p.handlers[roomName]
|
||||
proxy, prefix, ok := p.handlers.Match(r.URL.Path)
|
||||
p.mu.RUnlock()
|
||||
|
||||
// if room not found
|
||||
@@ -190,7 +161,7 @@ func (p *ProxyManagerCtx) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// redirect to room ending with /
|
||||
if doRedir {
|
||||
if r.URL.Path == prefix {
|
||||
r.URL.Path += "/"
|
||||
http.Redirect(w, r, r.URL.String(), http.StatusTemporaryRedirect)
|
||||
return
|
||||
@@ -208,12 +179,11 @@ func (p *ProxyManagerCtx) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
RoomNotReady(w, r)
|
||||
}
|
||||
|
||||
p.logger.Err(err).Str("room", roomName).Msg("proxying error")
|
||||
p.logger.Err(err).Str("prefix", prefix).Msg("proxying error")
|
||||
}
|
||||
|
||||
// strip prefix from proxy
|
||||
pathPrefix := path.Join(p.prefix, roomName)
|
||||
handler := http.StripPrefix(pathPrefix, proxy)
|
||||
handler := http.StripPrefix(prefix, proxy)
|
||||
|
||||
// handle by proxy
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type prefixHandler struct {
|
||||
Value *httputil.ReverseProxy
|
||||
Children map[string]*prefixHandler
|
||||
}
|
||||
|
||||
func (p *prefixHandler) Set(prefix string, value *httputil.ReverseProxy) {
|
||||
arr := strings.Split(prefix, "/")
|
||||
len := len(arr)
|
||||
|
||||
for i, a := range arr {
|
||||
if a == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Children == nil {
|
||||
p.Children = map[string]*prefixHandler{}
|
||||
}
|
||||
|
||||
dat, ok := p.Children[a]
|
||||
if !ok {
|
||||
dat = &prefixHandler{}
|
||||
}
|
||||
|
||||
if i == len-1 {
|
||||
dat.Value = value
|
||||
dat.Children = nil
|
||||
}
|
||||
|
||||
p.Children[a] = dat
|
||||
p = dat
|
||||
}
|
||||
}
|
||||
|
||||
func (p *prefixHandler) Remove(prefix string) {
|
||||
arr := strings.Split(prefix, "/")
|
||||
len := len(arr)
|
||||
|
||||
for i, a := range arr {
|
||||
if a == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if i == len-1 {
|
||||
delete(p.Children, a)
|
||||
}
|
||||
|
||||
dat, ok := p.Children[a]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
p = dat
|
||||
}
|
||||
|
||||
// TODO: Remove all empty references.
|
||||
}
|
||||
|
||||
func (p *prefixHandler) Match(path string) (*httputil.ReverseProxy, string, bool) {
|
||||
arr := strings.Split(path, "/")
|
||||
prefixArr := []string{}
|
||||
|
||||
for _, a := range arr {
|
||||
if a == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
pointer, ok := p.Children[a]
|
||||
if !ok {
|
||||
return nil, "", false
|
||||
}
|
||||
|
||||
p = pointer
|
||||
prefixArr = append(prefixArr, a)
|
||||
|
||||
// if leaf node
|
||||
if p.Children == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if not leaf node
|
||||
if p.Children != nil {
|
||||
return nil, "", false
|
||||
}
|
||||
|
||||
prefix := "/" + strings.Join(prefixArr, "/")
|
||||
return p.Value, prefix, true
|
||||
}
|
||||
@@ -30,7 +30,9 @@ func (manager *RoomManagerCtx) extractLabels(labels map[string]string) (*RoomLab
|
||||
|
||||
url, ok := labels["m1k1o.neko_rooms.url"]
|
||||
if !ok {
|
||||
// It should be always available.
|
||||
url = manager.config.GetRoomUrl(name)
|
||||
//return nil, fmt.Errorf("damaged container labels: url not found")
|
||||
}
|
||||
|
||||
nekoImage, ok := labels["m1k1o.neko_rooms.neko_image"]
|
||||
@@ -113,15 +115,11 @@ func (manager *RoomManagerCtx) extractLabels(labels map[string]string) (*RoomLab
|
||||
func (manager *RoomManagerCtx) serializeLabels(labels RoomLabels) map[string]string {
|
||||
labelsMap := map[string]string{
|
||||
"m1k1o.neko_rooms.name": labels.Name,
|
||||
"m1k1o.neko_rooms.url": manager.config.GetRoomUrl(labels.Name),
|
||||
"m1k1o.neko_rooms.instance": manager.config.InstanceName,
|
||||
"m1k1o.neko_rooms.neko_image": labels.NekoImage,
|
||||
}
|
||||
|
||||
// Only when using traefik is the URL fixed with the room itself and not with neko-rooms.
|
||||
if manager.config.Traefik.Enabled {
|
||||
labelsMap["m1k1o.neko_rooms.url"] = manager.config.GetRoomUrl(labels.Name)
|
||||
}
|
||||
|
||||
if labels.Mux && labels.Epr.Min == labels.Epr.Max {
|
||||
labelsMap["m1k1o.neko_rooms.mux"] = fmt.Sprintf("%d", labels.Epr.Min)
|
||||
} else {
|
||||
|
||||
@@ -181,9 +181,9 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro
|
||||
// Set traefik labels
|
||||
//
|
||||
|
||||
if t := manager.config.Traefik; t.Enabled {
|
||||
pathPrefix := path.Join("/", manager.config.PathPrefix, roomName)
|
||||
|
||||
if t := manager.config.Traefik; t.Enabled {
|
||||
// create traefik rule
|
||||
traefikRule := "PathPrefix(`" + pathPrefix + "`)"
|
||||
if t.Domain != "" && t.Domain != "*" {
|
||||
@@ -217,7 +217,9 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro
|
||||
labels["traefik.http.routers."+containerName+".tls.certresolver"] = t.Certresolver
|
||||
}
|
||||
} else {
|
||||
labels["m1k1o.neko_rooms.host"] = fmt.Sprintf("%s:%d", containerName, frontendPort)
|
||||
labels["m1k1o.neko_rooms.proxy"] = "true"
|
||||
labels["m1k1o.neko_rooms.proxy.path"] = pathPrefix
|
||||
labels["m1k1o.neko_rooms.proxy.port"] = fmt.Sprintf("%d", frontendPort)
|
||||
}
|
||||
|
||||
// add custom labels
|
||||
|
||||
@@ -59,7 +59,7 @@ func New(ApiManager types.ApiManager, roomConfig *config.Room, config *config.Se
|
||||
// rooms page
|
||||
//
|
||||
|
||||
router.Route(roomConfig.PathPrefix, func(r chi.Router) {
|
||||
router.Group(func(r chi.Router) {
|
||||
if !roomConfig.Traefik.Enabled {
|
||||
r.Handle("/*", proxyHandler)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user