add low power state for testing

Former-commit-id: 996fe59999
This commit is contained in:
miloschwartz
2026-01-13 14:30:02 -08:00
parent 8a788ef238
commit dada0cc124
3 changed files with 218 additions and 6 deletions

View File

@@ -53,6 +53,11 @@ var (
updateRegister func(newData interface{})
stopPing chan struct{}
peerManager *peers.PeerManager
// Power mode management
currentPowerMode string
originalPeerInterval time.Duration
originalHolepunchMinInterval time.Duration
originalHolepunchMaxInterval time.Duration
)
// initTunnelInfo creates the shared UDP socket and holepunch manager.
@@ -112,7 +117,7 @@ func Init(ctx context.Context, config GlobalConfig) {
}
}()
}
if config.LogFilePath != "" {
logFile, err := os.OpenFile(config.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
if err != nil {
@@ -432,6 +437,18 @@ func StartTunnel(config TunnelConfig) {
APIServer: apiServer,
})
// Capture original intervals for power mode management
if peerManager != nil {
peerMonitor := peerManager.GetPeerMonitor()
if peerMonitor != nil {
originalPeerInterval = 2 * time.Second // Default peer interval
originalHolepunchMinInterval, originalHolepunchMaxInterval = peerMonitor.GetHolepunchIntervals()
}
}
// Initialize power mode to normal
currentPowerMode = "normal"
for i := range wgData.Sites {
site := wgData.Sites[i]
var siteEndpoint string
@@ -1156,3 +1173,172 @@ func SwitchOrg(orgID string) error {
return nil
}
// SetPowerMode switches between normal and low power modes
// In low power mode: websocket is closed (stopping pings) and monitoring intervals are set to 10 minutes
// In normal power mode: websocket is reconnected (restarting pings) and monitoring intervals are restored
func SetPowerMode(mode string) error {
// Validate mode
if mode != "normal" && mode != "low" {
return fmt.Errorf("invalid power mode: %s (must be 'normal' or 'low')", mode)
}
// If already in the requested mode, return early
if currentPowerMode == mode {
logger.Debug("Already in %s power mode", mode)
return nil
}
logger.Info("Switching to %s power mode", mode)
if mode == "low" {
// Low Power Mode: Close websocket and reduce monitoring frequency
// Close websocket connection - this stops:
// - WebSocket ping monitor (via pingMonitor() goroutine)
// - Application ping messages (via keepSendingPing() goroutine)
if olmClient != nil {
logger.Info("Closing websocket connection for low power mode")
if err := olmClient.Close(); err != nil {
logger.Error("Error closing websocket: %v", err)
}
}
// Stop application ping goroutine
if stopPing != nil {
select {
case <-stopPing:
// Channel already closed
default:
close(stopPing)
}
}
// Stop peer monitoring
if peerManager != nil {
peerManager.Stop()
}
// Store original intervals if not already stored
if originalPeerInterval == 0 && peerManager != nil {
peerMonitor := peerManager.GetPeerMonitor()
if peerMonitor != nil {
originalPeerInterval = 2 * time.Second // Default peer interval
originalHolepunchMinInterval, originalHolepunchMaxInterval = peerMonitor.GetHolepunchIntervals()
}
}
// Set monitoring intervals to 10 minutes
if peerManager != nil {
peerMonitor := peerManager.GetPeerMonitor()
if peerMonitor != nil {
lowPowerInterval := 10 * time.Minute
peerMonitor.SetInterval(lowPowerInterval)
peerMonitor.SetHolepunchInterval(lowPowerInterval, lowPowerInterval)
logger.Info("Set monitoring intervals to 10 minutes for low power mode")
}
}
// Restart peer monitoring with new intervals (but websocket remains closed)
if peerManager != nil {
peerManager.Start()
}
currentPowerMode = "low"
logger.Info("Switched to low power mode")
} else {
// Normal Power Mode: Restore intervals and reconnect websocket
// Restore monitoring intervals to original values
if peerManager != nil {
peerMonitor := peerManager.GetPeerMonitor()
if peerMonitor != nil {
// Restore peer interval
if originalPeerInterval == 0 {
originalPeerInterval = 2 * time.Second // Default if not captured
}
peerMonitor.SetInterval(originalPeerInterval)
// Restore holepunch intervals
if originalHolepunchMinInterval == 0 {
originalHolepunchMinInterval = 2 * time.Second // Default if not captured
}
if originalHolepunchMaxInterval == 0 {
originalHolepunchMaxInterval = 30 * time.Second // Default if not captured
}
peerMonitor.SetHolepunchInterval(originalHolepunchMinInterval, originalHolepunchMaxInterval)
logger.Info("Restored monitoring intervals to normal (peer: %v, holepunch: %v-%v)",
originalPeerInterval, originalHolepunchMinInterval, originalHolepunchMaxInterval)
}
}
// Restart peer monitoring with restored intervals
if peerManager != nil {
peerManager.Start()
}
// Reconnect websocket - this restarts:
// - WebSocket ping monitor
// - Application ping messages (via OnConnect callback)
// Note: Since websocket client's Close() permanently closes the done channel,
// we need to create a new client instance and re-register handlers
if tunnelConfig.ID != "" && tunnelConfig.Secret != "" && tunnelConfig.Endpoint != "" {
logger.Info("Reconnecting websocket for normal power mode")
// Close old client if it exists
if olmClient != nil {
olmClient.Close()
}
// Recreate stopPing channel for application pings
stopPing = make(chan struct{})
// Create a new websocket client
var (
id = tunnelConfig.ID
secret = tunnelConfig.Secret
userToken = tunnelConfig.UserToken
)
olm, err := websocket.NewClient(
id,
secret,
userToken,
tunnelConfig.OrgID,
tunnelConfig.Endpoint,
tunnelConfig.PingIntervalDuration,
tunnelConfig.PingTimeoutDuration,
)
if err != nil {
logger.Error("Failed to create new websocket client: %v", err)
return fmt.Errorf("failed to create new websocket client: %w", err)
}
// Store the new client
olmClient = olm
// Re-register essential handlers (simplified - only the most critical ones)
// The full handler registration happens in StartTunnel, so this is just for reconnection
olm.OnConnect(func() error {
logger.Info("Websocket Reconnected")
apiServer.SetConnectionStatus(true)
go keepSendingPing(olm)
return nil
})
// Connect to the WebSocket server
if err := olm.Connect(); err != nil {
logger.Error("Failed to reconnect websocket: %v", err)
return fmt.Errorf("failed to reconnect websocket: %w", err)
}
} else {
logger.Warn("Cannot reconnect websocket: tunnel config not available")
}
currentPowerMode = "normal"
logger.Info("Switched to normal power mode")
}
return nil
}

View File

@@ -84,6 +84,13 @@ func (pm *PeerManager) GetPeer(siteId int) (SiteConfig, bool) {
return peer, ok
}
// GetPeerMonitor returns the internal peer monitor instance
func (pm *PeerManager) GetPeerMonitor() *monitor.PeerMonitor {
pm.mu.RLock()
defer pm.mu.RUnlock()
return pm.peerMonitor
}
func (pm *PeerManager) GetAllPeers() []SiteConfig {
pm.mu.RLock()
defer pm.mu.RUnlock()

View File

@@ -62,11 +62,11 @@ type PeerMonitor struct {
holepunchFailures map[int]int // siteID -> consecutive failure count
// Exponential backoff fields for holepunch monitor
holepunchMinInterval time.Duration // Minimum interval (initial)
holepunchMaxInterval time.Duration // Maximum interval (cap for backoff)
holepunchBackoffMultiplier float64 // Multiplier for each stable check
holepunchStableCount map[int]int // siteID -> consecutive stable status count
holepunchCurrentInterval time.Duration // Current interval with backoff applied
holepunchMinInterval time.Duration // Minimum interval (initial)
holepunchMaxInterval time.Duration // Maximum interval (cap for backoff)
holepunchBackoffMultiplier float64 // Multiplier for each stable check
holepunchStableCount map[int]int // siteID -> consecutive stable status count
holepunchCurrentInterval time.Duration // Current interval with backoff applied
// Rapid initial test fields
rapidTestInterval time.Duration // interval between rapid test attempts
@@ -167,6 +167,25 @@ func (pm *PeerMonitor) SetMaxAttempts(attempts int) {
}
}
// SetHolepunchInterval sets both the minimum and maximum intervals for holepunch monitoring
func (pm *PeerMonitor) SetHolepunchInterval(minInterval, maxInterval time.Duration) {
pm.mutex.Lock()
defer pm.mutex.Unlock()
pm.holepunchMinInterval = minInterval
pm.holepunchMaxInterval = maxInterval
// Reset current interval to the new minimum
pm.holepunchCurrentInterval = minInterval
}
// GetHolepunchIntervals returns the current minimum and maximum intervals for holepunch monitoring
func (pm *PeerMonitor) GetHolepunchIntervals() (minInterval, maxInterval time.Duration) {
pm.mutex.Lock()
defer pm.mutex.Unlock()
return pm.holepunchMinInterval, pm.holepunchMaxInterval
}
// AddPeer adds a new peer to monitor
func (pm *PeerMonitor) AddPeer(siteID int, endpoint string, holepunchEndpoint string) error {
pm.mutex.Lock()