29bcf4403e
gates / gates (push) Successful in 1m37s
GET /host/line-test reports waiting_for_quiet (due, in the window, tunnel up, only the quiet missing); POST /host/line-test/run runs one measurement with a 5 s settle and a 10 s quiet check instead of 10 quiet minutes, runs 5 s apart (~55 s). Every other weekly rule holds. Commit only; the release rides the next train.
90 lines
3.7 KiB
Go
90 lines
3.7 KiB
Go
package localapi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/linetest"
|
|
)
|
|
|
|
// LineTestReader is the R-1247 weekly upload line test (internal/linetest.Tester) as the local API reads it.
|
|
type LineTestReader interface {
|
|
Value() (mbps float64, at time.Time, ok bool)
|
|
}
|
|
|
|
// LineTestRequester is the R-1250 half: a due test the uplink never lets start, and a test on request. The Tester
|
|
// satisfies it; a reader without it answers waiting_for_quiet:false and 503 on the POST.
|
|
type LineTestRequester interface {
|
|
WaitingForQuiet() bool
|
|
RunNow(ctx context.Context) (mbps float64, runs []float64, err error)
|
|
}
|
|
|
|
// LineTestResponse is GET /host/line-test. Present=false: never measured, or the job is not wired (no tunnel, disabled,
|
|
// or an agent before the route answers 404 — the controller reads all three as „unknown").
|
|
type LineTestResponse struct {
|
|
Present bool `json:"present"`
|
|
Mbps float64 `json:"mbps,omitempty"`
|
|
MeasuredAt string `json:"measured_at,omitempty"` // RFC3339 UTC
|
|
Method string `json:"method,omitempty"` // "iperf3-tunnel": a 10-second upload to ep0 inside the tunnel
|
|
// WaitingForQuiet: a measurement is due and could start now, but the uplink has not been quiet for 10 minutes
|
|
// (R-1250). The controller with a seeding torrent app then holds its limit and POSTs /host/line-test/run.
|
|
WaitingForQuiet bool `json:"waiting_for_quiet,omitempty"`
|
|
}
|
|
|
|
// LineTestRunResponse is POST /host/line-test/run. OK=false with Refused: nothing ran (Refused is linetest.Refusal's
|
|
// code, or "server-busy"); OK=false without it: the runs failed.
|
|
type LineTestRunResponse struct {
|
|
OK bool `json:"ok"`
|
|
Mbps float64 `json:"mbps,omitempty"`
|
|
RunsMbps []float64 `json:"runs_mbps,omitempty"`
|
|
Refused string `json:"refused,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// SetLineTest wires the weekly line test (main.go builds it after the local API server).
|
|
func (s *Server) SetLineTest(r LineTestReader) { s.lineTest = r }
|
|
|
|
func (s *Server) handleLineTest(w http.ResponseWriter, r *http.Request, vmid int) {
|
|
if s.lineTest == nil {
|
|
writeOK(w, LineTestResponse{Present: false})
|
|
return
|
|
}
|
|
var waiting bool
|
|
if q, ok := s.lineTest.(LineTestRequester); ok {
|
|
waiting = q.WaitingForQuiet()
|
|
}
|
|
mbps, at, ok := s.lineTest.Value()
|
|
if !ok {
|
|
writeOK(w, LineTestResponse{Present: false, WaitingForQuiet: waiting})
|
|
return
|
|
}
|
|
s.logger.Debug("local-api: /host/line-test served", "vmid", vmid, "mbps", mbps, "measured_at", at.UTC().Format(time.RFC3339), "waiting_for_quiet", waiting)
|
|
writeOK(w, LineTestResponse{Present: true, Mbps: mbps, MeasuredAt: at.UTC().Format(time.RFC3339), Method: "iperf3-tunnel", WaitingForQuiet: waiting})
|
|
}
|
|
|
|
// handleLineTestRun is POST /host/line-test/run (R-1250): a test on request, synchronous (about 55 s). The answer is
|
|
// always 200 with the outcome in the body, except 503 when no line test is wired.
|
|
func (s *Server) handleLineTestRun(w http.ResponseWriter, r *http.Request, vmid int) {
|
|
q, ok := s.lineTest.(LineTestRequester)
|
|
if s.lineTest == nil || !ok {
|
|
writeErr(w, http.StatusServiceUnavailable, "line test not configured")
|
|
return
|
|
}
|
|
s.logger.Info("local-api: line test requested", "vmid", vmid)
|
|
mbps, runs, err := q.RunNow(r.Context())
|
|
var ref *linetest.Refusal
|
|
switch {
|
|
case err == nil:
|
|
writeOK(w, LineTestRunResponse{OK: true, Mbps: mbps, RunsMbps: runs})
|
|
case errors.As(err, &ref):
|
|
s.logger.Info("local-api: line test request refused", "vmid", vmid, "reason", ref.Code, "detail", ref.Msg)
|
|
writeOK(w, LineTestRunResponse{Refused: ref.Code, Error: ref.Msg})
|
|
case errors.Is(err, linetest.ErrBusy):
|
|
writeOK(w, LineTestRunResponse{Refused: "server-busy", Error: err.Error()})
|
|
default:
|
|
writeOK(w, LineTestRunResponse{Error: err.Error()})
|
|
}
|
|
}
|