This commit is contained in:
@@ -22,6 +22,14 @@ import (
|
|||||||
"git.send.nrw/sendnrw/decent-webui/internal/mesh"
|
"git.send.nrw/sendnrw/decent-webui/internal/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func parseDuration(s string, def time.Duration) time.Duration {
|
||||||
|
d, err := time.ParseDuration(strings.TrimSpace(s))
|
||||||
|
if err != nil || d <= 0 {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
/*** Config ***/
|
/*** Config ***/
|
||||||
func loadConfig() AppConfig {
|
func loadConfig() AppConfig {
|
||||||
// HTTP
|
// HTTP
|
||||||
@@ -44,6 +52,9 @@ func loadConfig() AppConfig {
|
|||||||
DiscoveryAddress: getenvDefault("MESH_DISCOVERY_ADDR", "239.8.8.8:9898"),
|
DiscoveryAddress: getenvDefault("MESH_DISCOVERY_ADDR", "239.8.8.8:9898"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.PeerTTL = parseDuration(os.Getenv("MESH_PEER_TTL"), 2*time.Minute)
|
||||||
|
m.PruneInterval = parseDuration(os.Getenv("MESH_PRUNE_INTERVAL"), 30*time.Second)
|
||||||
|
|
||||||
// Wenn keine AdvertURL gesetzt ist, versuche eine sinnvolle Herleitung:
|
// Wenn keine AdvertURL gesetzt ist, versuche eine sinnvolle Herleitung:
|
||||||
if strings.TrimSpace(m.AdvertURL) == "" {
|
if strings.TrimSpace(m.AdvertURL) == "" {
|
||||||
m.AdvertURL = inferAdvertURL(m.BindAddr)
|
m.AdvertURL = inferAdvertURL(m.BindAddr)
|
||||||
@@ -440,6 +451,10 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("mesh init: %v", err)
|
log.Fatalf("mesh init: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hintergrund-Pruner starten
|
||||||
|
mnode.StartPeerPruner()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Printf("[mesh] listening on %s advertise %s seeds=%v discovery=%v",
|
log.Printf("[mesh] listening on %s advertise %s seeds=%v discovery=%v",
|
||||||
cfg.Mesh.BindAddr, cfg.Mesh.AdvertURL, cfg.Mesh.Seeds, cfg.Mesh.EnableDiscovery)
|
cfg.Mesh.BindAddr, cfg.Mesh.AdvertURL, cfg.Mesh.Seeds, cfg.Mesh.EnableDiscovery)
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ type Config struct {
|
|||||||
Seeds []string // other peers' mesh base URLs
|
Seeds []string // other peers' mesh base URLs
|
||||||
ClusterSecret string // HMAC key
|
ClusterSecret string // HMAC key
|
||||||
EnableDiscovery bool
|
EnableDiscovery bool
|
||||||
DiscoveryAddress string // "239.8.8.8:9898"
|
DiscoveryAddress string // "239.8.8.8:9898"
|
||||||
|
PeerTTL time.Duration // wie lange darf ein Peer inaktiv sein (Default siehe unten)
|
||||||
|
PruneInterval time.Duration // wie oft wird gepruned
|
||||||
}
|
}
|
||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
@@ -72,6 +74,78 @@ type Node struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemovePeer löscht einen Peer aus der Peer-Tabelle. Seeds werden standardmäßig nicht entfernt.
|
||||||
|
func (n *Node) RemovePeer(url string) bool {
|
||||||
|
n.mu.Lock()
|
||||||
|
defer n.mu.Unlock()
|
||||||
|
if url == "" || url == n.self.URL {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Seeds schützen
|
||||||
|
if n.isSeed(url) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, ok := n.peers[url]; ok {
|
||||||
|
delete(n.peers, url)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PruneNow entfernt alle Peers, deren LastSeen vor cutoff liegt (Seeds bleiben).
|
||||||
|
func (n *Node) PruneNow(cutoff time.Time) int {
|
||||||
|
n.mu.Lock()
|
||||||
|
defer n.mu.Unlock()
|
||||||
|
removed := 0
|
||||||
|
for url, p := range n.peers {
|
||||||
|
if url == n.self.URL || n.isSeed(url) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.LastSeen.IsZero() || p.LastSeen.Before(cutoff) {
|
||||||
|
delete(n.peers, url)
|
||||||
|
removed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartPeerPruner startet den Hintergrundjob (stoppt automatisch bei n.stop).
|
||||||
|
func (n *Node) StartPeerPruner() {
|
||||||
|
go n.loopPrunePeers()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) loopPrunePeers() {
|
||||||
|
ttl := n.cfg.PeerTTL
|
||||||
|
if ttl <= 0 {
|
||||||
|
ttl = 2 * time.Minute
|
||||||
|
}
|
||||||
|
interval := n.cfg.PruneInterval
|
||||||
|
if interval <= 0 {
|
||||||
|
interval = 30 * time.Second
|
||||||
|
}
|
||||||
|
t := time.NewTicker(interval)
|
||||||
|
defer t.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-n.stop:
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
cutoff := time.Now().Add(-ttl)
|
||||||
|
_ = n.PruneNow(cutoff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper: ist url ein Seed?
|
||||||
|
func (n *Node) isSeed(url string) bool {
|
||||||
|
for _, s := range n.cfg.Seeds {
|
||||||
|
if strings.TrimSpace(s) == strings.TrimSpace(url) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func New(cfg Config, cbs Callbacks) (*Node, error) {
|
func New(cfg Config, cbs Callbacks) (*Node, error) {
|
||||||
if cfg.BindAddr == "" || cfg.AdvertURL == "" {
|
if cfg.BindAddr == "" || cfg.AdvertURL == "" {
|
||||||
return nil, errors.New("mesh: BindAddr and AdvertURL required")
|
return nil, errors.New("mesh: BindAddr and AdvertURL required")
|
||||||
|
|||||||
Reference in New Issue
Block a user