Files
lpra/internal/agent/autoupdate.go
T

96 lines
2.4 KiB
Go

package agent
import (
"context"
"log"
"net/http"
"strconv"
"sync"
"time"
"github.com/x-grk/longpolling-remote-access/internal/proto"
)
// Auto-update, the agent's part: the server tells (with every poll, on its
// config and on the WebSocket) its own version and how often to compare -
// in minutes, 0 for never. When the server's version is newer, the agent
// takes the new one from it, as the panel's «Обновить» does.
// autoUpdate is what the agent was told, and when it last looked.
type autoUpdate struct {
mu sync.Mutex
minutes int
serverVer string
last time.Time
}
func (a *Agent) applyUpdateInfo(minutes int, serverVersion string) {
a.auto.mu.Lock()
defer a.auto.mu.Unlock()
if minutes < 0 {
minutes = 0
}
a.auto.minutes = minutes
if serverVersion != "" {
a.auto.serverVer = serverVersion
}
}
// applyUpdateHeaders takes the settings from the headers of an answer; an
// answer without them (an older server) changes nothing.
func (a *Agent) applyUpdateHeaders(h http.Header) {
v := h.Get(proto.AutoUpdateHeader)
if v == "" {
return
}
m, err := strconv.Atoi(v)
if err != nil {
m = 0
}
a.applyUpdateInfo(m, h.Get(proto.ServerVersionHeader))
}
func (a *Agent) updateInfo() (int, string) {
a.auto.mu.Lock()
defer a.auto.mu.Unlock()
return a.auto.minutes, a.auto.serverVer
}
func (a *Agent) autoUpdateOn() bool { m, _ := a.updateInfo(); return m > 0 }
// autoUpdateTick: one look at the clock. Out of the interval since the last
// look, it looks; the server's version newer than its own, it updates.
// Reports whether it did.
func (a *Agent) autoUpdateTick(now time.Time, update func() proto.TaskResult) bool {
a.auto.mu.Lock()
if a.auto.minutes <= 0 || (!a.auto.last.IsZero() && now.Sub(a.auto.last) < time.Duration(a.auto.minutes)*time.Minute) {
a.auto.mu.Unlock()
return false
}
a.auto.last = now
server := a.auto.serverVer
a.auto.mu.Unlock()
if !proto.VersionNewer(server, Version) {
return false
}
log.Printf("auto-update: server has %s, this is %s - updating", server, Version)
if res := update(); !res.OK {
log.Printf("auto-update: %s", res.Error)
}
return true
}
// autoUpdateLoop watches the clock until ctx ends.
func (a *Agent) autoUpdateLoop(ctx context.Context) {
t := time.NewTicker(30 * time.Second)
defer t.Stop()
for {
select {
case <-ctx.Done():
return
case now := <-t.C:
a.autoUpdateTick(now, a.doUpdate)
}
}
}