feat: machine status page, speed test, live presence, forwards without ufw (0.0.5)
Status page per machine: CPU load, RAM and disk of the client, sampled by the agent only while the page is open and visible (one reading serves every open tab), plus a button that has the agent measure its link to the host: ping, down and up. Online/offline now comes from the control polls the hub holds open, so a dropped agent shows offline at once instead of 45 s later, and the dashboard keeps the status column live. A forward rule can be created "without ufw": the panel opens nothing in ufw for it and so revokes nothing when it is deleted. The checkbox is shown only while the ufw integration is on. Dashboard row actions are icons; a status action is added.
This commit is contained in:
1 parent
7f4d645f19
commit
86cb0226ab
22 files changed
+920
-34
No files matched your search
+62
-3
@@ -22,6 +22,12 @@ type Hub struct {
|
||||
|
||||
sessions map[string]*Session
|
||||
tasks map[string]*taskEntry
|
||||
|
||||
// Presence: control polls held open right now per agent, and when each
|
||||
// agent's last one ended. started is when this hub came up.
|
||||
polls map[string]int
|
||||
lastPoll map[string]time.Time
|
||||
started time.Time
|
||||
}
|
||||
|
||||
// taskEntry is a pending one-shot task awaiting the agent's result.
|
||||
@@ -52,6 +58,9 @@ func NewHub() *Hub {
|
||||
ctrlWaiters: map[string]chan struct{}{},
|
||||
sessions: map[string]*Session{},
|
||||
tasks: map[string]*taskEntry{},
|
||||
polls: map[string]int{},
|
||||
lastPoll: map[string]time.Time{},
|
||||
started: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +106,56 @@ func (h *Hub) CreateForward(agentID string, port int) *Session {
|
||||
return s
|
||||
}
|
||||
|
||||
// WaitControl blocks until a job is queued for agentID or timeout elapses.
|
||||
func (h *Hub) WaitControl(agentID string, timeout time.Duration) proto.ControlMessage {
|
||||
deadline := time.After(timeout)
|
||||
// pollGrace is how long an agent stays online between two control polls: it
|
||||
// re-polls at once, so this only has to cover a slow round trip or one retry.
|
||||
const pollGrace = 10 * time.Second
|
||||
|
||||
// Online reports whether the agent is connected: it holds a control poll open
|
||||
// or has just finished one. ok is false while that cannot be told yet (right
|
||||
// after a start, before agents had time to come back).
|
||||
func (h *Hub) Online(agentID string) (online, ok bool) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
if h.polls[agentID] > 0 {
|
||||
return true, true
|
||||
}
|
||||
if t, seen := h.lastPoll[agentID]; seen {
|
||||
return time.Since(t) < pollGrace, true
|
||||
}
|
||||
return false, time.Since(h.started) > pollHold+pollGrace
|
||||
}
|
||||
|
||||
// Forget drops what the hub keeps about a deleted agent.
|
||||
func (h *Hub) Forget(agentID string) {
|
||||
h.mu.Lock()
|
||||
delete(h.jobs, agentID)
|
||||
delete(h.ctrlWaiters, agentID)
|
||||
delete(h.lastPoll, agentID)
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// WaitControl blocks until a job is queued for agentID, the timeout elapses
|
||||
// or the agent hangs up (done). The agent counts as online meanwhile.
|
||||
func (h *Hub) WaitControl(done <-chan struct{}, agentID string, timeout time.Duration) proto.ControlMessage {
|
||||
h.mu.Lock()
|
||||
h.polls[agentID]++
|
||||
h.mu.Unlock()
|
||||
defer func() {
|
||||
h.mu.Lock()
|
||||
if h.polls[agentID]--; h.polls[agentID] <= 0 {
|
||||
delete(h.polls, agentID)
|
||||
}
|
||||
// A poll cut off by the agent is not a sign of life.
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
h.lastPoll[agentID] = time.Now()
|
||||
}
|
||||
h.mu.Unlock()
|
||||
}()
|
||||
t := time.NewTimer(timeout)
|
||||
defer t.Stop()
|
||||
deadline := t.C
|
||||
for {
|
||||
h.mu.Lock()
|
||||
if q := h.jobs[agentID]; len(q) > 0 {
|
||||
@@ -119,6 +175,9 @@ func (h *Hub) WaitControl(agentID string, timeout time.Duration) proto.ControlMe
|
||||
case <-w:
|
||||
case <-deadline:
|
||||
return proto.ControlMessage{Type: proto.CtrlIdle}
|
||||
case <-done:
|
||||
// The job, if one comes, stays queued for the next poll.
|
||||
return proto.ControlMessage{Type: proto.CtrlIdle}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user