mirror of
https://github.com/m1k1o/neko-rooms.git
synced 2026-11-03 07:01:35 -06:00
proxy use room manager.
This commit is contained in:
+11
-1
@@ -59,6 +59,8 @@ func RoomNotFound(w http.ResponseWriter, r *http.Request, waitEnabled bool) {
|
|||||||
|
|
||||||
if waitEnabled {
|
if waitEnabled {
|
||||||
roomWait(w, r)
|
roomWait(w, r)
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(`<meta http-equiv="refresh" content="60">`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,10 +83,12 @@ func RoomNotRunning(w http.ResponseWriter, r *http.Request, waitEnabled bool) {
|
|||||||
|
|
||||||
if waitEnabled {
|
if waitEnabled {
|
||||||
roomWait(w, r)
|
roomWait(w, r)
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(`<meta http-equiv="refresh" content="10">`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RoomNotReady(w http.ResponseWriter, r *http.Request) {
|
func RoomNotReady(w http.ResponseWriter, r *http.Request, waitEnabled bool) {
|
||||||
utils.Swal2Response(w, `
|
utils.Swal2Response(w, `
|
||||||
<meta http-equiv="refresh" content="2">
|
<meta http-equiv="refresh" content="2">
|
||||||
|
|
||||||
@@ -102,6 +106,12 @@ func RoomNotReady(w http.ResponseWriter, r *http.Request) {
|
|||||||
<button type="button" onclick="location = location" class="swal2-confirm swal2-styled" style="margin-top: 1.25em">Reload</button>
|
<button type="button" onclick="location = location" class="swal2-confirm swal2-styled" style="margin-top: 1.25em">Reload</button>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
if waitEnabled {
|
||||||
|
roomWait(w, r)
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(`<meta http-equiv="refresh" content="2">`))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RoomReady(w http.ResponseWriter, r *http.Request) {
|
func RoomReady(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
+53
-124
@@ -2,7 +2,6 @@ package proxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -11,18 +10,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
dockerClient "github.com/docker/docker/client"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"github.com/m1k1o/neko-rooms/internal/room"
|
||||||
|
"github.com/m1k1o/neko-rooms/internal/types"
|
||||||
"github.com/m1k1o/neko-rooms/pkg/prefix"
|
"github.com/m1k1o/neko-rooms/pkg/prefix"
|
||||||
)
|
)
|
||||||
|
|
||||||
type entry struct {
|
type entry struct {
|
||||||
id string
|
id string
|
||||||
running bool
|
running bool
|
||||||
|
ready bool
|
||||||
handler *httputil.ReverseProxy
|
handler *httputil.ReverseProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,20 +40,18 @@ type ProxyManagerCtx struct {
|
|||||||
waitChans map[string]*wait
|
waitChans map[string]*wait
|
||||||
waitEnabled bool
|
waitEnabled bool
|
||||||
|
|
||||||
client *dockerClient.Client
|
rooms *room.RoomManagerCtx
|
||||||
instanceName string
|
handlers prefix.Tree[*entry]
|
||||||
handlers prefix.Tree[*entry]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(client *dockerClient.Client, instanceName string, waitEnabled bool) *ProxyManagerCtx {
|
func New(rooms *room.RoomManagerCtx, waitEnabled bool) *ProxyManagerCtx {
|
||||||
return &ProxyManagerCtx{
|
return &ProxyManagerCtx{
|
||||||
logger: log.With().Str("module", "proxy").Logger(),
|
logger: log.With().Str("module", "proxy").Logger(),
|
||||||
waitChans: map[string]*wait{},
|
waitChans: map[string]*wait{},
|
||||||
|
|
||||||
client: client,
|
rooms: rooms,
|
||||||
instanceName: instanceName,
|
waitEnabled: waitEnabled,
|
||||||
waitEnabled: waitEnabled,
|
handlers: prefix.NewTree[*entry](),
|
||||||
handlers: prefix.NewTree[*entry](),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,47 +64,31 @@ func (p *ProxyManagerCtx) Start() {
|
|||||||
p.logger.Err(err).Msg("unable to refresh containers")
|
p.logger.Err(err).Msg("unable to refresh containers")
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs, errs := p.client.Events(p.ctx, dockerTypes.EventsOptions{
|
msgs, errs := p.rooms.Events(p.ctx)
|
||||||
Filters: filters.NewArgs(
|
|
||||||
filters.Arg("type", "container"),
|
|
||||||
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.instanceName)),
|
|
||||||
filters.Arg("event", "create"),
|
|
||||||
filters.Arg("event", "start"),
|
|
||||||
//filters.Arg("event", "health_status"),
|
|
||||||
filters.Arg("event", "stop"),
|
|
||||||
filters.Arg("event", "destroy"),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case err, ok := <-errs:
|
case err, ok := <-errs:
|
||||||
if !ok {
|
if !ok {
|
||||||
p.logger.Fatal().Msg("docker event error channel closed")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p.logger.Err(err).Msg("got docker event error")
|
p.logger.Err(err).Msg("room event error")
|
||||||
case msg, ok := <-msgs:
|
case msg, ok := <-msgs:
|
||||||
if !ok {
|
enabled, path, port, ok := p.parseLabels(msg.ContainerLabels)
|
||||||
p.logger.Fatal().Msg("docker event channel closed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled, path, port, ok := p.parseLabels(msg.Actor.Attributes)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
host := msg.ID[:12] + ":" + port
|
host := msg.ID + ":" + port
|
||||||
|
|
||||||
p.logger.Info().
|
p.logger.Info().
|
||||||
Str("action", msg.Action).
|
Str("action", string(msg.Action)).
|
||||||
Str("path", path).
|
Str("path", path).
|
||||||
Str("host", host).
|
Str("host", host).
|
||||||
Msg("got docker event")
|
Msg("got room event")
|
||||||
|
|
||||||
// terminate waiting for any events
|
// terminate waiting for any event
|
||||||
if p.waitEnabled {
|
if p.waitEnabled {
|
||||||
p.waitMu.Lock()
|
p.waitMu.Lock()
|
||||||
ch, ok := p.waitChans[path]
|
ch, ok := p.waitChans[path]
|
||||||
@@ -120,15 +101,22 @@ func (p *ProxyManagerCtx) Start() {
|
|||||||
|
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
switch msg.Action {
|
switch msg.Action {
|
||||||
case "create":
|
case types.RoomEventCreated:
|
||||||
p.handlers.Insert(path, &entry{
|
p.handlers.Insert(path, &entry{
|
||||||
id: msg.ID,
|
id: msg.ID,
|
||||||
running: false,
|
running: false,
|
||||||
})
|
})
|
||||||
case "start":
|
case types.RoomEventStarted:
|
||||||
|
p.handlers.Insert(path, &entry{
|
||||||
|
id: msg.ID,
|
||||||
|
running: true,
|
||||||
|
ready: false,
|
||||||
|
})
|
||||||
|
case types.RoomEventReady:
|
||||||
e := &entry{
|
e := &entry{
|
||||||
id: msg.ID,
|
id: msg.ID,
|
||||||
running: true,
|
running: true,
|
||||||
|
ready: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if proxying is disabled
|
// if proxying is disabled
|
||||||
@@ -140,12 +128,12 @@ func (p *ProxyManagerCtx) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.handlers.Insert(path, e)
|
p.handlers.Insert(path, e)
|
||||||
case "stop":
|
case types.RoomEventStopped:
|
||||||
p.handlers.Insert(path, &entry{
|
p.handlers.Insert(path, &entry{
|
||||||
id: msg.ID,
|
id: msg.ID,
|
||||||
running: false,
|
running: false,
|
||||||
})
|
})
|
||||||
case "destroy":
|
case types.RoomEventDestroyed:
|
||||||
p.handlers.Remove(path)
|
p.handlers.Remove(path)
|
||||||
}
|
}
|
||||||
p.mu.Unlock()
|
p.mu.Unlock()
|
||||||
@@ -163,41 +151,33 @@ func (p *ProxyManagerCtx) Refresh() error {
|
|||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
containers, err := p.client.ContainerList(p.ctx, dockerTypes.ContainerListOptions{
|
rooms, err := p.rooms.List(p.ctx, nil)
|
||||||
All: true,
|
|
||||||
Filters: filters.NewArgs(
|
|
||||||
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.instanceName)),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.handlers = prefix.NewTree[*entry]()
|
p.handlers = prefix.NewTree[*entry]()
|
||||||
|
|
||||||
for _, cont := range containers {
|
for _, room := range rooms {
|
||||||
enabled, path, port, ok := p.parseLabels(cont.Labels)
|
enabled, path, port, ok := p.parseLabels(room.ContainerLabels)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
host := cont.ID[:12] + ":" + port
|
host := room.ID + ":" + port
|
||||||
|
|
||||||
entry := &entry{
|
entry := &entry{
|
||||||
id: cont.ID,
|
id: room.ID,
|
||||||
|
running: room.Running,
|
||||||
|
ready: room.IsReady,
|
||||||
}
|
}
|
||||||
|
|
||||||
if cont.State == "running" {
|
// if proxying is enabled and room is ready
|
||||||
entry.running = true
|
if enabled && room.IsReady {
|
||||||
|
entry.handler = httputil.NewSingleHostReverseProxy(&url.URL{
|
||||||
// if proxying is disabled
|
Scheme: "http",
|
||||||
if enabled {
|
Host: host,
|
||||||
entry.handler = httputil.NewSingleHostReverseProxy(&url.URL{
|
})
|
||||||
Scheme: "http",
|
|
||||||
Host: host,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.handlers.Insert(path, entry)
|
p.handlers.Insert(path, entry)
|
||||||
@@ -207,41 +187,9 @@ func (p *ProxyManagerCtx) Refresh() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *ProxyManagerCtx) parseLabels(labels map[string]string) (enabled bool, path string, port string, ok bool) {
|
func (p *ProxyManagerCtx) parseLabels(labels map[string]string) (enabled bool, path string, port string, ok bool) {
|
||||||
enabledStr, found := labels["m1k1o.neko_rooms.proxy.enabled"]
|
var enabledStr string
|
||||||
if !found {
|
enabledStr, ok = labels["m1k1o.neko_rooms.proxy.enabled"]
|
||||||
//
|
if !ok {
|
||||||
// workaround for legacy traefik labels
|
|
||||||
//
|
|
||||||
|
|
||||||
// get room name
|
|
||||||
var roomName string
|
|
||||||
roomName, ok = labels["m1k1o.neko_rooms.name"]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get container name
|
|
||||||
containerName := p.instanceName + "-" + roomName
|
|
||||||
|
|
||||||
// get path
|
|
||||||
path, ok = labels["traefik.http.middlewares."+containerName+"-prf.stripprefix.prefixes"]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove last /
|
|
||||||
path = strings.TrimSuffix(path, "/")
|
|
||||||
|
|
||||||
// get port
|
|
||||||
port, ok = labels["traefik.http.services."+containerName+"-frontend.loadbalancer.server.port"]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// workaround for legacy traefik labels
|
|
||||||
//
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,50 +267,31 @@ func (p *ProxyManagerCtx) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if room not running
|
// if room not running
|
||||||
if !proxy.running {
|
if !proxy.running || !proxy.ready {
|
||||||
// blocking until room is running
|
// blocking until room is ready
|
||||||
if r.URL.Query().Has("wait") && p.waitEnabled {
|
if r.URL.Query().Has("wait") && p.waitEnabled {
|
||||||
p.waitForPath(w, r, cleanPath)
|
p.waitForPath(w, r, cleanPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomNotRunning(w, r, p.waitEnabled)
|
if !proxy.running {
|
||||||
|
RoomNotRunning(w, r, p.waitEnabled)
|
||||||
|
} else {
|
||||||
|
RoomNotReady(w, r, p.waitEnabled)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not proxying, check for container readiness status
|
// if not proxying, just return room ready
|
||||||
if proxy.handler == nil {
|
if proxy.handler == nil {
|
||||||
containers, err := p.client.ContainerList(p.ctx, dockerTypes.ContainerListOptions{
|
RoomReady(w, r)
|
||||||
All: true,
|
|
||||||
Filters: filters.NewArgs(
|
|
||||||
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.instanceName)),
|
|
||||||
filters.Arg("id", proxy.id),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil || len(containers) != 1 {
|
|
||||||
p.logger.Err(err).Msg("error while getting container ready status")
|
|
||||||
RoomNotReady(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
container := containers[0]
|
|
||||||
if strings.Contains(container.Status, "starting") {
|
|
||||||
RoomNotReady(w, r)
|
|
||||||
} else {
|
|
||||||
RoomReady(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle not ready room
|
// handle not ready room
|
||||||
proxy.handler.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
|
proxy.handler.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
if r.URL.Path == "/" {
|
|
||||||
RoomNotReady(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.logger.Err(err).Str("prefix", prefix).Msg("proxying error")
|
p.logger.Err(err).Str("prefix", prefix).Msg("proxying error")
|
||||||
|
http.Error(w, "unable to connect to room", http.StatusBadGateway)
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip prefix from proxy
|
// strip prefix from proxy
|
||||||
|
|||||||
@@ -144,8 +144,7 @@ func (main *MainCtx) Start() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
main.proxyManager = proxy.New(
|
main.proxyManager = proxy.New(
|
||||||
client,
|
main.roomManager,
|
||||||
main.Configs.Room.InstanceName,
|
|
||||||
main.Configs.Room.WaitEnabled,
|
main.Configs.Room.WaitEnabled,
|
||||||
)
|
)
|
||||||
main.proxyManager.Start()
|
main.proxyManager.Start()
|
||||||
|
|||||||
Reference in New Issue
Block a user