mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 09:36:56 +00:00
feat: add WebRTC configuration support for gateway instances
This commit is contained in:
parent
552fde428e
commit
3597c61cfc
1
.gitignore
vendored
1
.gitignore
vendored
@ -93,3 +93,4 @@ terms-agreement
|
|||||||
./cli
|
./cli
|
||||||
./inspector
|
./inspector
|
||||||
docs/later_todos/
|
docs/later_todos/
|
||||||
|
sim/
|
||||||
@ -48,6 +48,11 @@ type SpawnRequest struct {
|
|||||||
IPFSAPIURL string `json:"ipfs_api_url,omitempty"`
|
IPFSAPIURL string `json:"ipfs_api_url,omitempty"`
|
||||||
IPFSTimeout string `json:"ipfs_timeout,omitempty"`
|
IPFSTimeout string `json:"ipfs_timeout,omitempty"`
|
||||||
IPFSReplicationFactor int `json:"ipfs_replication_factor,omitempty"`
|
IPFSReplicationFactor int `json:"ipfs_replication_factor,omitempty"`
|
||||||
|
// Gateway WebRTC config (when action = "spawn-gateway" and WebRTC is enabled)
|
||||||
|
GatewayWebRTCEnabled bool `json:"gateway_webrtc_enabled,omitempty"`
|
||||||
|
GatewaySFUPort int `json:"gateway_sfu_port,omitempty"`
|
||||||
|
GatewayTURNDomain string `json:"gateway_turn_domain,omitempty"`
|
||||||
|
GatewayTURNSecret string `json:"gateway_turn_secret,omitempty"`
|
||||||
|
|
||||||
// SFU config (when action = "spawn-sfu")
|
// SFU config (when action = "spawn-sfu")
|
||||||
SFUListenAddr string `json:"sfu_listen_addr,omitempty"`
|
SFUListenAddr string `json:"sfu_listen_addr,omitempty"`
|
||||||
@ -225,6 +230,10 @@ func (h *SpawnHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
IPFSAPIURL: req.IPFSAPIURL,
|
IPFSAPIURL: req.IPFSAPIURL,
|
||||||
IPFSTimeout: ipfsTimeout,
|
IPFSTimeout: ipfsTimeout,
|
||||||
IPFSReplicationFactor: req.IPFSReplicationFactor,
|
IPFSReplicationFactor: req.IPFSReplicationFactor,
|
||||||
|
WebRTCEnabled: req.GatewayWebRTCEnabled,
|
||||||
|
SFUPort: req.GatewaySFUPort,
|
||||||
|
TURNDomain: req.GatewayTURNDomain,
|
||||||
|
TURNSecret: req.GatewayTURNSecret,
|
||||||
}
|
}
|
||||||
if err := h.systemdSpawner.SpawnGateway(ctx, req.Namespace, req.NodeID, cfg); err != nil {
|
if err := h.systemdSpawner.SpawnGateway(ctx, req.Namespace, req.NodeID, cfg); err != nil {
|
||||||
h.logger.Error("Failed to spawn Gateway instance", zap.Error(err))
|
h.logger.Error("Failed to spawn Gateway instance", zap.Error(err))
|
||||||
@ -241,6 +250,51 @@ func (h *SpawnHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
writeSpawnResponse(w, http.StatusOK, SpawnResponse{Success: true})
|
writeSpawnResponse(w, http.StatusOK, SpawnResponse{Success: true})
|
||||||
|
|
||||||
|
case "restart-gateway":
|
||||||
|
// Restart gateway with updated config (used by EnableWebRTC/DisableWebRTC)
|
||||||
|
var ipfsTimeout time.Duration
|
||||||
|
if req.IPFSTimeout != "" {
|
||||||
|
var err error
|
||||||
|
ipfsTimeout, err = time.ParseDuration(req.IPFSTimeout)
|
||||||
|
if err != nil {
|
||||||
|
ipfsTimeout = 60 * time.Second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var olricTimeout time.Duration
|
||||||
|
if req.GatewayOlricTimeout != "" {
|
||||||
|
var err error
|
||||||
|
olricTimeout, err = time.ParseDuration(req.GatewayOlricTimeout)
|
||||||
|
if err != nil {
|
||||||
|
olricTimeout = 30 * time.Second
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
olricTimeout = 30 * time.Second
|
||||||
|
}
|
||||||
|
cfg := gateway.InstanceConfig{
|
||||||
|
Namespace: req.Namespace,
|
||||||
|
NodeID: req.NodeID,
|
||||||
|
HTTPPort: req.GatewayHTTPPort,
|
||||||
|
BaseDomain: req.GatewayBaseDomain,
|
||||||
|
RQLiteDSN: req.GatewayRQLiteDSN,
|
||||||
|
GlobalRQLiteDSN: req.GatewayGlobalRQLiteDSN,
|
||||||
|
OlricServers: req.GatewayOlricServers,
|
||||||
|
OlricTimeout: olricTimeout,
|
||||||
|
IPFSClusterAPIURL: req.IPFSClusterAPIURL,
|
||||||
|
IPFSAPIURL: req.IPFSAPIURL,
|
||||||
|
IPFSTimeout: ipfsTimeout,
|
||||||
|
IPFSReplicationFactor: req.IPFSReplicationFactor,
|
||||||
|
WebRTCEnabled: req.GatewayWebRTCEnabled,
|
||||||
|
SFUPort: req.GatewaySFUPort,
|
||||||
|
TURNDomain: req.GatewayTURNDomain,
|
||||||
|
TURNSecret: req.GatewayTURNSecret,
|
||||||
|
}
|
||||||
|
if err := h.systemdSpawner.RestartGateway(ctx, req.Namespace, req.NodeID, cfg); err != nil {
|
||||||
|
h.logger.Error("Failed to restart Gateway instance", zap.Error(err))
|
||||||
|
writeSpawnResponse(w, http.StatusInternalServerError, SpawnResponse{Error: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeSpawnResponse(w, http.StatusOK, SpawnResponse{Success: true})
|
||||||
|
|
||||||
case "save-cluster-state":
|
case "save-cluster-state":
|
||||||
if len(req.ClusterState) == 0 {
|
if len(req.ClusterState) == 0 {
|
||||||
writeSpawnResponse(w, http.StatusBadRequest, SpawnResponse{Error: "cluster_state is required"})
|
writeSpawnResponse(w, http.StatusBadRequest, SpawnResponse{Error: "cluster_state is required"})
|
||||||
|
|||||||
@ -90,6 +90,20 @@ type InstanceConfig struct {
|
|||||||
IPFSAPIURL string // IPFS API URL (e.g., "http://localhost:5001")
|
IPFSAPIURL string // IPFS API URL (e.g., "http://localhost:5001")
|
||||||
IPFSTimeout time.Duration // Timeout for IPFS operations
|
IPFSTimeout time.Duration // Timeout for IPFS operations
|
||||||
IPFSReplicationFactor int // IPFS replication factor
|
IPFSReplicationFactor int // IPFS replication factor
|
||||||
|
// WebRTC configuration (populated when WebRTC is enabled for the namespace)
|
||||||
|
WebRTCEnabled bool // Enable WebRTC (SFU/TURN) routes on this gateway
|
||||||
|
SFUPort int // SFU signaling port on this node
|
||||||
|
TURNDomain string // TURN server domain (e.g., "turn.ns-alice.orama-devnet.network")
|
||||||
|
TURNSecret string // TURN shared secret for credential generation
|
||||||
|
}
|
||||||
|
|
||||||
|
// GatewayYAMLWebRTC represents the webrtc section of the gateway YAML config.
|
||||||
|
// Must match yamlWebRTCCfg in cmd/gateway/config.go.
|
||||||
|
type GatewayYAMLWebRTC struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
SFUPort int `yaml:"sfu_port,omitempty"`
|
||||||
|
TURNDomain string `yaml:"turn_domain,omitempty"`
|
||||||
|
TURNSecret string `yaml:"turn_secret,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GatewayYAMLConfig represents the gateway YAML configuration structure
|
// GatewayYAMLConfig represents the gateway YAML configuration structure
|
||||||
@ -110,6 +124,7 @@ type GatewayYAMLConfig struct {
|
|||||||
IPFSAPIURL string `yaml:"ipfs_api_url,omitempty"`
|
IPFSAPIURL string `yaml:"ipfs_api_url,omitempty"`
|
||||||
IPFSTimeout string `yaml:"ipfs_timeout,omitempty"`
|
IPFSTimeout string `yaml:"ipfs_timeout,omitempty"`
|
||||||
IPFSReplicationFactor int `yaml:"ipfs_replication_factor,omitempty"`
|
IPFSReplicationFactor int `yaml:"ipfs_replication_factor,omitempty"`
|
||||||
|
WebRTC GatewayYAMLWebRTC `yaml:"webrtc,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInstanceSpawner creates a new Gateway instance spawner
|
// NewInstanceSpawner creates a new Gateway instance spawner
|
||||||
@ -294,6 +309,12 @@ func (is *InstanceSpawner) generateConfig(configPath string, cfg InstanceConfig,
|
|||||||
IPFSClusterAPIURL: cfg.IPFSClusterAPIURL,
|
IPFSClusterAPIURL: cfg.IPFSClusterAPIURL,
|
||||||
IPFSAPIURL: cfg.IPFSAPIURL,
|
IPFSAPIURL: cfg.IPFSAPIURL,
|
||||||
IPFSReplicationFactor: cfg.IPFSReplicationFactor,
|
IPFSReplicationFactor: cfg.IPFSReplicationFactor,
|
||||||
|
WebRTC: GatewayYAMLWebRTC{
|
||||||
|
Enabled: cfg.WebRTCEnabled,
|
||||||
|
SFUPort: cfg.SFUPort,
|
||||||
|
TURNDomain: cfg.TURNDomain,
|
||||||
|
TURNSecret: cfg.TURNSecret,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
// Set Olric timeout if provided
|
// Set Olric timeout if provided
|
||||||
if cfg.OlricTimeout > 0 {
|
if cfg.OlricTimeout > 0 {
|
||||||
|
|||||||
@ -661,6 +661,10 @@ func (cm *ClusterManager) spawnGatewayRemote(ctx context.Context, nodeIP string,
|
|||||||
"ipfs_api_url": cfg.IPFSAPIURL,
|
"ipfs_api_url": cfg.IPFSAPIURL,
|
||||||
"ipfs_timeout": ipfsTimeout,
|
"ipfs_timeout": ipfsTimeout,
|
||||||
"ipfs_replication_factor": cfg.IPFSReplicationFactor,
|
"ipfs_replication_factor": cfg.IPFSReplicationFactor,
|
||||||
|
"gateway_webrtc_enabled": cfg.WebRTCEnabled,
|
||||||
|
"gateway_sfu_port": cfg.SFUPort,
|
||||||
|
"gateway_turn_domain": cfg.TURNDomain,
|
||||||
|
"gateway_turn_secret": cfg.TURNSecret,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1555,6 +1559,16 @@ func (cm *ClusterManager) restoreClusterOnNode(ctx context.Context, clusterID, n
|
|||||||
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add WebRTC config if enabled for this namespace
|
||||||
|
if webrtcCfg, err := cm.GetWebRTCConfig(ctx, namespaceName); err == nil && webrtcCfg != nil {
|
||||||
|
if sfuBlock, err := cm.webrtcPortAllocator.GetSFUPorts(ctx, clusterID, cm.localNodeID); err == nil && sfuBlock != nil {
|
||||||
|
gwCfg.WebRTCEnabled = true
|
||||||
|
gwCfg.SFUPort = sfuBlock.SFUSignalingPort
|
||||||
|
gwCfg.TURNDomain = fmt.Sprintf("turn.ns-%s.%s", namespaceName, cm.baseDomain)
|
||||||
|
gwCfg.TURNSecret = webrtcCfg.TURNSharedSecret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := cm.spawnGatewayWithSystemd(ctx, gwCfg); err != nil {
|
if err := cm.spawnGatewayWithSystemd(ctx, gwCfg); err != nil {
|
||||||
cm.logger.Error("Failed to restore Gateway", zap.String("namespace", namespaceName), zap.Error(err))
|
cm.logger.Error("Failed to restore Gateway", zap.String("namespace", namespaceName), zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
@ -1617,7 +1631,8 @@ type ClusterLocalState struct {
|
|||||||
// WebRTC fields (zero values when WebRTC not enabled — backward compatible)
|
// WebRTC fields (zero values when WebRTC not enabled — backward compatible)
|
||||||
HasSFU bool `json:"has_sfu,omitempty"`
|
HasSFU bool `json:"has_sfu,omitempty"`
|
||||||
HasTURN bool `json:"has_turn,omitempty"`
|
HasTURN bool `json:"has_turn,omitempty"`
|
||||||
TURNSharedSecret string `json:"-"` // Never persisted to disk state file
|
TURNSharedSecret string `json:"turn_shared_secret,omitempty"` // Needed for gateway to generate TURN credentials on cold start
|
||||||
|
TURNDomain string `json:"turn_domain,omitempty"` // TURN server domain for gateway config
|
||||||
TURNCredentialTTL int `json:"turn_credential_ttl,omitempty"`
|
TURNCredentialTTL int `json:"turn_credential_ttl,omitempty"`
|
||||||
SFUSignalingPort int `json:"sfu_signaling_port,omitempty"`
|
SFUSignalingPort int `json:"sfu_signaling_port,omitempty"`
|
||||||
SFUMediaPortStart int `json:"sfu_media_port_start,omitempty"`
|
SFUMediaPortStart int `json:"sfu_media_port_start,omitempty"`
|
||||||
@ -1915,6 +1930,15 @@ func (cm *ClusterManager) restoreClusterFromState(ctx context.Context, state *Cl
|
|||||||
IPFSTimeout: cm.ipfsTimeout,
|
IPFSTimeout: cm.ipfsTimeout,
|
||||||
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add WebRTC config from persisted local state
|
||||||
|
if state.HasSFU && state.SFUSignalingPort > 0 && state.TURNSharedSecret != "" {
|
||||||
|
gwCfg.WebRTCEnabled = true
|
||||||
|
gwCfg.SFUPort = state.SFUSignalingPort
|
||||||
|
gwCfg.TURNDomain = state.TURNDomain
|
||||||
|
gwCfg.TURNSecret = state.TURNSharedSecret
|
||||||
|
}
|
||||||
|
|
||||||
if err := cm.spawnGatewayWithSystemd(ctx, gwCfg); err != nil {
|
if err := cm.spawnGatewayWithSystemd(ctx, gwCfg); err != nil {
|
||||||
cm.logger.Error("Failed to restore Gateway from state", zap.String("namespace", state.NamespaceName), zap.Error(err))
|
cm.logger.Error("Failed to restore Gateway from state", zap.String("namespace", state.NamespaceName), zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/DeBrosOfficial/network/pkg/client"
|
"github.com/DeBrosOfficial/network/pkg/client"
|
||||||
|
"github.com/DeBrosOfficial/network/pkg/gateway"
|
||||||
"github.com/DeBrosOfficial/network/pkg/sfu"
|
"github.com/DeBrosOfficial/network/pkg/sfu"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@ -189,7 +190,10 @@ func (cm *ClusterManager) EnableWebRTC(ctx context.Context, namespaceName, enabl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 14. Update cluster-state.json on all nodes with WebRTC info
|
// 14. Update cluster-state.json on all nodes with WebRTC info
|
||||||
cm.updateClusterStateWithWebRTC(ctx, cluster, clusterNodes, sfuBlocks, turnBlocks)
|
cm.updateClusterStateWithWebRTC(ctx, cluster, clusterNodes, sfuBlocks, turnBlocks, turnDomain, turnSecret)
|
||||||
|
|
||||||
|
// 15. Restart namespace gateways with WebRTC config so they register WebRTC routes
|
||||||
|
cm.restartGatewaysWithWebRTC(ctx, cluster, clusterNodes, nodePortBlocks, sfuBlocks, turnDomain, turnSecret)
|
||||||
|
|
||||||
cm.logEvent(ctx, cluster.ID, EventWebRTCEnabled, "",
|
cm.logEvent(ctx, cluster.ID, EventWebRTCEnabled, "",
|
||||||
fmt.Sprintf("WebRTC enabled: SFU on %d nodes, TURN on %d nodes", len(clusterNodes), len(turnNodes)), nil)
|
fmt.Sprintf("WebRTC enabled: SFU on %d nodes, TURN on %d nodes", len(clusterNodes), len(turnNodes)), nil)
|
||||||
@ -265,7 +269,19 @@ func (cm *ClusterManager) DisableWebRTC(ctx context.Context, namespaceName strin
|
|||||||
cm.db.Exec(internalCtx, `DELETE FROM namespace_webrtc_config WHERE namespace_cluster_id = ?`, cluster.ID)
|
cm.db.Exec(internalCtx, `DELETE FROM namespace_webrtc_config WHERE namespace_cluster_id = ?`, cluster.ID)
|
||||||
|
|
||||||
// 9. Update cluster-state.json to remove WebRTC info
|
// 9. Update cluster-state.json to remove WebRTC info
|
||||||
cm.updateClusterStateWithWebRTC(ctx, cluster, clusterNodes, nil, nil)
|
cm.updateClusterStateWithWebRTC(ctx, cluster, clusterNodes, nil, nil, "", "")
|
||||||
|
|
||||||
|
// 10. Restart namespace gateways without WebRTC config so they unregister WebRTC routes
|
||||||
|
portBlocks, err := cm.portAllocator.GetAllPortBlocks(ctx, cluster.ID)
|
||||||
|
if err == nil {
|
||||||
|
nodePortBlocks := make(map[string]*PortBlock)
|
||||||
|
for i := range portBlocks {
|
||||||
|
nodePortBlocks[portBlocks[i].NodeID] = &portBlocks[i]
|
||||||
|
}
|
||||||
|
cm.restartGatewaysWithWebRTC(ctx, cluster, clusterNodes, nodePortBlocks, nil, "", "")
|
||||||
|
} else {
|
||||||
|
cm.logger.Warn("Failed to get port blocks for gateway restart after WebRTC disable", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
cm.logEvent(ctx, cluster.ID, EventWebRTCDisabled, "", "WebRTC disabled", nil)
|
cm.logEvent(ctx, cluster.ID, EventWebRTCDisabled, "", "WebRTC disabled", nil)
|
||||||
|
|
||||||
@ -508,13 +524,14 @@ func (cm *ClusterManager) cleanupWebRTCOnError(ctx context.Context, clusterID, n
|
|||||||
|
|
||||||
// updateClusterStateWithWebRTC updates the cluster-state.json on all nodes
|
// updateClusterStateWithWebRTC updates the cluster-state.json on all nodes
|
||||||
// to include (or remove) WebRTC port information.
|
// to include (or remove) WebRTC port information.
|
||||||
// Pass nil maps to clear WebRTC state (when disabling).
|
// Pass nil maps and empty strings to clear WebRTC state (when disabling).
|
||||||
func (cm *ClusterManager) updateClusterStateWithWebRTC(
|
func (cm *ClusterManager) updateClusterStateWithWebRTC(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cluster *NamespaceCluster,
|
cluster *NamespaceCluster,
|
||||||
nodes []clusterNodeInfo,
|
nodes []clusterNodeInfo,
|
||||||
sfuBlocks map[string]*WebRTCPortBlock,
|
sfuBlocks map[string]*WebRTCPortBlock,
|
||||||
turnBlocks map[string]*WebRTCPortBlock,
|
turnBlocks map[string]*WebRTCPortBlock,
|
||||||
|
turnDomain, turnSecret string,
|
||||||
) {
|
) {
|
||||||
// Get existing port blocks for base state
|
// Get existing port blocks for base state
|
||||||
portBlocks, err := cm.portAllocator.GetAllPortBlocks(ctx, cluster.ID)
|
portBlocks, err := cm.portAllocator.GetAllPortBlocks(ctx, cluster.ID)
|
||||||
@ -589,6 +606,9 @@ func (cm *ClusterManager) updateClusterStateWithWebRTC(
|
|||||||
state.TURNRelayPortEnd = turnBlock.TURNRelayPortEnd
|
state.TURNRelayPortEnd = turnBlock.TURNRelayPortEnd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Persist TURN domain and secret so gateways can be restored on cold start
|
||||||
|
state.TURNDomain = turnDomain
|
||||||
|
state.TURNSharedSecret = turnSecret
|
||||||
|
|
||||||
if node.NodeID == cm.localNodeID {
|
if node.NodeID == cm.localNodeID {
|
||||||
if err := cm.saveLocalState(state); err != nil {
|
if err := cm.saveLocalState(state); err != nil {
|
||||||
@ -615,3 +635,118 @@ func (cm *ClusterManager) saveRemoteState(ctx context.Context, nodeIP, namespace
|
|||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restartGatewaysWithWebRTC restarts namespace gateways on all nodes with updated WebRTC config.
|
||||||
|
// Pass nil sfuBlocks and empty turnDomain/turnSecret to disable WebRTC on gateways.
|
||||||
|
func (cm *ClusterManager) restartGatewaysWithWebRTC(
|
||||||
|
ctx context.Context,
|
||||||
|
cluster *NamespaceCluster,
|
||||||
|
nodes []clusterNodeInfo,
|
||||||
|
portBlocks map[string]*PortBlock,
|
||||||
|
sfuBlocks map[string]*WebRTCPortBlock,
|
||||||
|
turnDomain, turnSecret string,
|
||||||
|
) {
|
||||||
|
// Build Olric server addresses from port blocks + node IPs
|
||||||
|
var olricServers []string
|
||||||
|
for _, node := range nodes {
|
||||||
|
if pb, ok := portBlocks[node.NodeID]; ok {
|
||||||
|
olricServers = append(olricServers, fmt.Sprintf("%s:%d", node.InternalIP, pb.OlricHTTPPort))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
pb, ok := portBlocks[node.NodeID]
|
||||||
|
if !ok {
|
||||||
|
cm.logger.Warn("No port block for node, skipping gateway restart",
|
||||||
|
zap.String("node_id", node.NodeID))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build gateway config with WebRTC fields
|
||||||
|
webrtcEnabled := false
|
||||||
|
sfuPort := 0
|
||||||
|
if sfuBlocks != nil {
|
||||||
|
if sfuBlock, ok := sfuBlocks[node.NodeID]; ok {
|
||||||
|
webrtcEnabled = true
|
||||||
|
sfuPort = sfuBlock.SFUSignalingPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := gateway.InstanceConfig{
|
||||||
|
Namespace: cluster.NamespaceName,
|
||||||
|
NodeID: node.NodeID,
|
||||||
|
HTTPPort: pb.GatewayHTTPPort,
|
||||||
|
BaseDomain: cm.baseDomain,
|
||||||
|
RQLiteDSN: fmt.Sprintf("http://localhost:%d", pb.RQLiteHTTPPort),
|
||||||
|
GlobalRQLiteDSN: cm.globalRQLiteDSN,
|
||||||
|
OlricServers: olricServers,
|
||||||
|
OlricTimeout: 30 * time.Second,
|
||||||
|
IPFSClusterAPIURL: cm.ipfsClusterAPIURL,
|
||||||
|
IPFSAPIURL: cm.ipfsAPIURL,
|
||||||
|
IPFSTimeout: cm.ipfsTimeout,
|
||||||
|
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
||||||
|
WebRTCEnabled: webrtcEnabled,
|
||||||
|
SFUPort: sfuPort,
|
||||||
|
TURNDomain: turnDomain,
|
||||||
|
TURNSecret: turnSecret,
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.NodeID == cm.localNodeID {
|
||||||
|
if err := cm.systemdSpawner.RestartGateway(ctx, cluster.NamespaceName, node.NodeID, cfg); err != nil {
|
||||||
|
cm.logger.Error("Failed to restart local gateway with WebRTC config",
|
||||||
|
zap.String("namespace", cluster.NamespaceName),
|
||||||
|
zap.String("node_id", node.NodeID),
|
||||||
|
zap.Error(err))
|
||||||
|
} else {
|
||||||
|
cm.logger.Info("Restarted local gateway with WebRTC config",
|
||||||
|
zap.String("namespace", cluster.NamespaceName),
|
||||||
|
zap.Bool("webrtc_enabled", webrtcEnabled))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cm.restartGatewayRemote(ctx, node.InternalIP, cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restartGatewayRemote sends a restart-gateway request to a remote node.
|
||||||
|
func (cm *ClusterManager) restartGatewayRemote(ctx context.Context, nodeIP string, cfg gateway.InstanceConfig) {
|
||||||
|
ipfsTimeout := ""
|
||||||
|
if cfg.IPFSTimeout > 0 {
|
||||||
|
ipfsTimeout = cfg.IPFSTimeout.String()
|
||||||
|
}
|
||||||
|
olricTimeout := ""
|
||||||
|
if cfg.OlricTimeout > 0 {
|
||||||
|
olricTimeout = cfg.OlricTimeout.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := cm.sendSpawnRequest(ctx, nodeIP, map[string]interface{}{
|
||||||
|
"action": "restart-gateway",
|
||||||
|
"namespace": cfg.Namespace,
|
||||||
|
"node_id": cfg.NodeID,
|
||||||
|
"gateway_http_port": cfg.HTTPPort,
|
||||||
|
"gateway_base_domain": cfg.BaseDomain,
|
||||||
|
"gateway_rqlite_dsn": cfg.RQLiteDSN,
|
||||||
|
"gateway_global_rqlite_dsn": cfg.GlobalRQLiteDSN,
|
||||||
|
"gateway_olric_servers": cfg.OlricServers,
|
||||||
|
"gateway_olric_timeout": olricTimeout,
|
||||||
|
"ipfs_cluster_api_url": cfg.IPFSClusterAPIURL,
|
||||||
|
"ipfs_api_url": cfg.IPFSAPIURL,
|
||||||
|
"ipfs_timeout": ipfsTimeout,
|
||||||
|
"ipfs_replication_factor": cfg.IPFSReplicationFactor,
|
||||||
|
"gateway_webrtc_enabled": cfg.WebRTCEnabled,
|
||||||
|
"gateway_sfu_port": cfg.SFUPort,
|
||||||
|
"gateway_turn_domain": cfg.TURNDomain,
|
||||||
|
"gateway_turn_secret": cfg.TURNSecret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
cm.logger.Error("Failed to restart remote gateway with WebRTC config",
|
||||||
|
zap.String("node_ip", nodeIP),
|
||||||
|
zap.String("namespace", cfg.Namespace),
|
||||||
|
zap.Error(err))
|
||||||
|
} else {
|
||||||
|
cm.logger.Info("Restarted remote gateway with WebRTC config",
|
||||||
|
zap.String("node_ip", nodeIP),
|
||||||
|
zap.String("namespace", cfg.Namespace),
|
||||||
|
zap.Bool("webrtc_enabled", cfg.WebRTCEnabled))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -529,6 +529,16 @@ func (cm *ClusterManager) ReplaceClusterNode(ctx context.Context, cluster *Names
|
|||||||
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add WebRTC config if enabled for this namespace
|
||||||
|
if webrtcCfg, err := cm.GetWebRTCConfig(ctx, cluster.NamespaceName); err == nil && webrtcCfg != nil {
|
||||||
|
if sfuBlock, err := cm.webrtcPortAllocator.GetSFUPorts(ctx, cluster.ID, replacement.NodeID); err == nil && sfuBlock != nil {
|
||||||
|
gwCfg.WebRTCEnabled = true
|
||||||
|
gwCfg.SFUPort = sfuBlock.SFUSignalingPort
|
||||||
|
gwCfg.TURNDomain = fmt.Sprintf("turn.ns-%s.%s", cluster.NamespaceName, cm.baseDomain)
|
||||||
|
gwCfg.TURNSecret = webrtcCfg.TURNSharedSecret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var spawnErr error
|
var spawnErr error
|
||||||
if replacement.NodeID == cm.localNodeID {
|
if replacement.NodeID == cm.localNodeID {
|
||||||
spawnErr = cm.spawnGatewayWithSystemd(ctx, gwCfg)
|
spawnErr = cm.spawnGatewayWithSystemd(ctx, gwCfg)
|
||||||
@ -1061,6 +1071,16 @@ func (cm *ClusterManager) addNodeToCluster(
|
|||||||
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
IPFSReplicationFactor: cm.ipfsReplicationFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add WebRTC config if enabled for this namespace
|
||||||
|
if webrtcCfg, err := cm.GetWebRTCConfig(ctx, cluster.NamespaceName); err == nil && webrtcCfg != nil {
|
||||||
|
if sfuBlock, err := cm.webrtcPortAllocator.GetSFUPorts(ctx, cluster.ID, replacement.NodeID); err == nil && sfuBlock != nil {
|
||||||
|
gwCfg.WebRTCEnabled = true
|
||||||
|
gwCfg.SFUPort = sfuBlock.SFUSignalingPort
|
||||||
|
gwCfg.TURNDomain = fmt.Sprintf("turn.ns-%s.%s", cluster.NamespaceName, cm.baseDomain)
|
||||||
|
gwCfg.TURNSecret = webrtcCfg.TURNSharedSecret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if replacement.NodeID == cm.localNodeID {
|
if replacement.NodeID == cm.localNodeID {
|
||||||
spawnErr = cm.spawnGatewayWithSystemd(ctx, gwCfg)
|
spawnErr = cm.spawnGatewayWithSystemd(ctx, gwCfg)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -195,22 +195,8 @@ func (s *SystemdSpawner) SpawnGateway(ctx context.Context, namespace, nodeID str
|
|||||||
|
|
||||||
configPath := filepath.Join(configDir, fmt.Sprintf("gateway-%s.yaml", nodeID))
|
configPath := filepath.Join(configDir, fmt.Sprintf("gateway-%s.yaml", nodeID))
|
||||||
|
|
||||||
// Build Gateway YAML config
|
// Build Gateway YAML config using the shared type from gateway package
|
||||||
type gatewayYAMLConfig struct {
|
gatewayConfig := gateway.GatewayYAMLConfig{
|
||||||
ListenAddr string `yaml:"listen_addr"`
|
|
||||||
ClientNamespace string `yaml:"client_namespace"`
|
|
||||||
RQLiteDSN string `yaml:"rqlite_dsn"`
|
|
||||||
GlobalRQLiteDSN string `yaml:"global_rqlite_dsn,omitempty"`
|
|
||||||
DomainName string `yaml:"domain_name"`
|
|
||||||
OlricServers []string `yaml:"olric_servers"`
|
|
||||||
OlricTimeout string `yaml:"olric_timeout"`
|
|
||||||
IPFSClusterAPIURL string `yaml:"ipfs_cluster_api_url"`
|
|
||||||
IPFSAPIURL string `yaml:"ipfs_api_url"`
|
|
||||||
IPFSTimeout string `yaml:"ipfs_timeout"`
|
|
||||||
IPFSReplicationFactor int `yaml:"ipfs_replication_factor"`
|
|
||||||
}
|
|
||||||
|
|
||||||
gatewayConfig := gatewayYAMLConfig{
|
|
||||||
ListenAddr: fmt.Sprintf(":%d", cfg.HTTPPort),
|
ListenAddr: fmt.Sprintf(":%d", cfg.HTTPPort),
|
||||||
ClientNamespace: cfg.Namespace,
|
ClientNamespace: cfg.Namespace,
|
||||||
RQLiteDSN: cfg.RQLiteDSN,
|
RQLiteDSN: cfg.RQLiteDSN,
|
||||||
@ -222,6 +208,12 @@ func (s *SystemdSpawner) SpawnGateway(ctx context.Context, namespace, nodeID str
|
|||||||
IPFSAPIURL: cfg.IPFSAPIURL,
|
IPFSAPIURL: cfg.IPFSAPIURL,
|
||||||
IPFSTimeout: cfg.IPFSTimeout.String(),
|
IPFSTimeout: cfg.IPFSTimeout.String(),
|
||||||
IPFSReplicationFactor: cfg.IPFSReplicationFactor,
|
IPFSReplicationFactor: cfg.IPFSReplicationFactor,
|
||||||
|
WebRTC: gateway.GatewayYAMLWebRTC{
|
||||||
|
Enabled: cfg.WebRTCEnabled,
|
||||||
|
SFUPort: cfg.SFUPort,
|
||||||
|
TURNDomain: cfg.TURNDomain,
|
||||||
|
TURNSecret: cfg.TURNSecret,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
configBytes, err := yaml.Marshal(gatewayConfig)
|
configBytes, err := yaml.Marshal(gatewayConfig)
|
||||||
@ -291,6 +283,24 @@ func (s *SystemdSpawner) StopGateway(ctx context.Context, namespace, nodeID stri
|
|||||||
return s.systemdMgr.StopService(namespace, systemd.ServiceTypeGateway)
|
return s.systemdMgr.StopService(namespace, systemd.ServiceTypeGateway)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RestartGateway stops and re-spawns a Gateway instance with updated config.
|
||||||
|
// Used when gateway config changes at runtime (e.g., WebRTC enable/disable).
|
||||||
|
func (s *SystemdSpawner) RestartGateway(ctx context.Context, namespace, nodeID string, cfg gateway.InstanceConfig) error {
|
||||||
|
s.logger.Info("Restarting Gateway via systemd",
|
||||||
|
zap.String("namespace", namespace),
|
||||||
|
zap.String("node_id", nodeID))
|
||||||
|
|
||||||
|
// Stop existing service (ignore error if already stopped)
|
||||||
|
if err := s.systemdMgr.StopService(namespace, systemd.ServiceTypeGateway); err != nil {
|
||||||
|
s.logger.Warn("Failed to stop Gateway before restart (may not be running)",
|
||||||
|
zap.String("namespace", namespace),
|
||||||
|
zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-spawn with updated config
|
||||||
|
return s.SpawnGateway(ctx, namespace, nodeID, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
// SFUInstanceConfig holds configuration for spawning an SFU instance
|
// SFUInstanceConfig holds configuration for spawning an SFU instance
|
||||||
type SFUInstanceConfig struct {
|
type SFUInstanceConfig struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user