Files
Bridge-and-Join-s/internal/lkgateway/updprogress.go
T

117 lines
3.5 KiB
Go

package lkgateway
// Ход обновления для окна с индикатором на странице: этапы (проверка, скачивание, установка,
// перезапуск) и байты скачанного. Страница опрашивает GET /admin/setup/update/progress; когда
// обновляется ядро, bj-server перезапускается — страница видит обрыв связи, ждёт и сверяет
// версию работающего ядра с целевой.
import (
"encoding/json"
"net/http"
"os"
"path/filepath"
"sync"
"time"
)
type updateProgressState struct {
Seq int64 `json:"seq"`
Active bool `json:"active"`
Finished bool `json:"finished"`
Step int `json:"step"`
Steps int `json:"steps"`
Stage string `json:"stage"`
Done int64 `json:"done"`
Total int64 `json:"total"`
Target string `json:"target,omitempty"` // версия ядра после обновления (если ядро обновляется)
Result string `json:"result,omitempty"`
Note string `json:"note,omitempty"` // пояснение, которое нужно показать и после перезапуска ядра
Err string `json:"error,omitempty"`
Started time.Time `json:"started"`
Version string `json:"version"` // версия работающего bj-server
}
var updProg = struct {
sync.Mutex
s updateProgressState
}{}
func progressBegin(steps int) {
updProg.Lock()
defer updProg.Unlock()
updProg.s = updateProgressState{Seq: updProg.s.Seq + 1, Active: true, Steps: steps, Started: time.Now()}
}
func progressStage(step int, stage string) {
updProg.Lock()
defer updProg.Unlock()
updProg.s.Step, updProg.s.Stage = step, stage
}
func progressBytes(done, total int64) {
updProg.Lock()
defer updProg.Unlock()
updProg.s.Done, updProg.s.Total = done, total
}
func progressTarget(v string) {
updProg.Lock()
defer updProg.Unlock()
updProg.s.Target = v
}
func progressNote(n string) {
updProg.Lock()
defer updProg.Unlock()
updProg.s.Note = n
}
func progressEnd(result string, err error) {
updProg.Lock()
defer updProg.Unlock()
updProg.s.Active, updProg.s.Finished, updProg.s.Result = false, true, result
if err != nil {
updProg.s.Err = err.Error()
}
}
// progressWatchDir — пока идёт скачивание, раз в полсекунды считает байты в каталоге подготовки.
// Возвращает функцию остановки.
func progressWatchDir(dir string, total int64) func() {
stop := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
t := time.NewTicker(500 * time.Millisecond)
defer t.Stop()
for {
var n int64
_ = filepath.Walk(dir, func(_ string, fi os.FileInfo, err error) error {
if err == nil && fi.Mode().IsRegular() {
n += fi.Size()
}
return nil
})
progressBytes(n, total)
select {
case <-stop:
return
case <-t.C:
}
}
}()
return func() { close(stop); wg.Wait(); progressBytes(total, total) }
}
// updateProgressHandler — GET /admin/setup/update/progress.
func (h *setupHandlers) updateProgress(w http.ResponseWriter, r *http.Request) {
updProg.Lock()
s := updProg.s
updProg.Unlock()
s.Version = BuildVersion
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store")
_ = json.NewEncoder(w).Encode(s)
}