93 lines
3.0 KiB
Go
93 lines
3.0 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
|
|
"sultan/pkg/license"
|
|
"sultan/pkg/store"
|
|
"sultan/pkg/stream"
|
|
)
|
|
|
|
// ensureLease stellt die Lease eines verbundenen Knotens aus bzw. erneuert
|
|
// sie ab renew_after und schickt sie per lease_renew. Mit always wird auch
|
|
// eine noch gültige Lease erneut zugestellt (beim Verbinden).
|
|
func (s *Server) ensureLease(nodeID string, always bool) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
current, err := s.store.GetLease(ctx, nodeID)
|
|
if err == nil && now.Before(current.RenewAfter) {
|
|
if always {
|
|
s.sendLease(nodeID, []byte(current.Document))
|
|
}
|
|
return
|
|
}
|
|
if err != nil && !errors.Is(err, store.ErrNotFound) {
|
|
s.log.Error("Lease lesen", "node", nodeID, "err", err)
|
|
return
|
|
}
|
|
node, err := s.store.GetNode(ctx, nodeID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
tenant, err := s.store.GetTenant(ctx, node.TenantID)
|
|
if err != nil || tenant.Status == store.TenantQuarantined {
|
|
return
|
|
}
|
|
lic := licenseOf(tenant)
|
|
terms, ok := license.TermsFor(lic.Tier, node.Maintenance.Active(now), lic.ValidUntil, now, s.cfg.LeaseDuration, nil)
|
|
if !ok {
|
|
s.log.Warn("Keine Lease: Vertrag abgelaufen", "node", nodeID, "tenant", tenant.ID)
|
|
return
|
|
}
|
|
lease, err := (&license.Issuer{Key: s.master}).IssueLease(lic, nodeID, terms, now)
|
|
if err != nil {
|
|
s.log.Error("Lease ausstellen", "node", nodeID, "err", err)
|
|
return
|
|
}
|
|
doc, _ := json.Marshal(lease)
|
|
if err := s.store.PutLease(ctx, store.Lease{
|
|
NodeID: nodeID, IssuedAt: lease.IssuedAt, RenewAfter: lease.RenewAfter, ValidUntil: lease.ValidUntil, Document: string(doc),
|
|
}); err != nil {
|
|
s.log.Error("Lease speichern", "node", nodeID, "err", err)
|
|
return
|
|
}
|
|
s.log.Info("Lease ausgestellt", "node", nodeID, "valid_until", terms.ValidUntil, "renew_after", terms.RenewAfter)
|
|
s.sendLease(nodeID, doc)
|
|
}
|
|
|
|
func (s *Server) sendLease(nodeID string, doc []byte) {
|
|
if err := s.hub.Send(nodeID, stream.Event{Type: stream.EventLeaseRenew, Payload: doc}); err != nil {
|
|
s.log.Debug("Lease nicht zugestellt", "node", nodeID, "err", err)
|
|
}
|
|
}
|
|
|
|
// leaseValid meldet, ob der Knoten eine noch gültige Lease hat.
|
|
func (s *Server) leaseValid(nodeID string) bool {
|
|
l, err := s.store.GetLease(context.Background(), nodeID)
|
|
return err == nil && time.Now().Before(l.ValidUntil)
|
|
}
|
|
|
|
// dispatchCommands stellt Befehle aus der Warteschlange zu.
|
|
func (s *Server) dispatchCommands(nodeID string) {
|
|
ctx := context.Background()
|
|
cmds, err := s.store.PendingNodeCommands(ctx, nodeID)
|
|
if err != nil {
|
|
s.log.Error("Befehle lesen", "node", nodeID, "err", err)
|
|
return
|
|
}
|
|
for _, c := range cmds {
|
|
payload, _ := json.Marshal(stream.NodeCommand{Command: c.Command, Reason: c.Reason})
|
|
if err := s.hub.Send(nodeID, stream.Event{Type: stream.EventNodeCommand, Payload: payload}); err != nil {
|
|
return // später erneut (nächster Refresh oder nächste Verbindung)
|
|
}
|
|
if err := s.store.MarkNodeCommandSent(ctx, c.ID); err != nil {
|
|
s.log.Error("Befehl als gesendet markieren", "node", nodeID, "err", err)
|
|
return
|
|
}
|
|
s.log.Info("Befehl zugestellt", "node", nodeID, "command", c.Command, "reason", c.Reason)
|
|
}
|
|
}
|