Server side sorting for the websessions page. No longer using LinkedMap as a result.

This commit is contained in:
Erik Brakkee 2024-08-16 00:25:17 +02:00
parent 1de3c90146
commit 3574b64842
8 changed files with 55 additions and 34 deletions

View File

@ -21,7 +21,7 @@ func pageHandler(w http.ResponseWriter, r *http.Request) {
case "downloads.html": case "downloads.html":
templates2.DownloadsTab().Render(r.Context(), w) templates2.DownloadsTab().Render(r.Context(), w)
case "sessions.html": case "sessions.html":
templates2.SessionsTab(nil, access.Location).Render(r.Context(), w) templates2.SessionsTab(nil, nil, access.Location).Render(r.Context(), w)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }

View File

@ -24,10 +24,10 @@ func NewPrometheusState(state *models.State) *PrometheusState {
agents: collections.NewLinkedMap[models.AgentGuid, *models.Agent](), agents: collections.NewLinkedMap[models.AgentGuid, *models.Agent](),
clients: collections.NewLinkedMap[models.ClientGuid, *models.Client](), clients: collections.NewLinkedMap[models.ClientGuid, *models.Client](),
} }
for agent := range state.Agents.RangeValues() { for _, agent := range state.Agents {
res.agents.Put(agent.Guid, agent) res.agents.Put(agent.Guid, agent)
} }
for client := range state.Clients.RangeValues() { for _, client := range state.Clients {
res.clients.Put(client.Guid, client) res.clients.Put(client.Guid, client)
} }
return &res return &res

View File

@ -70,7 +70,7 @@ func main() {
render(dir, "downloads.html", templates2.Downloads) render(dir, "downloads.html", templates2.Downloads)
render(dir, "sessions-none.html", func() templ.Component { render(dir, "sessions-none.html", func() templ.Component {
return templates2.SessionsTab(nil, netherlands) return templates2.SessionsTab(nil, nil, netherlands)
}) })
render(dir, "sessions.html", func() templ.Component { render(dir, "sessions.html", func() templ.Component {
@ -94,7 +94,7 @@ func main() {
}, },
} }
agent.SetExpiryTime(time.Now().In(japan).Add(10 * time.Minute)) agent.SetExpiryTime(time.Now().In(japan).Add(10 * time.Minute))
state.Agents.Put(agent.Guid, &agent) state.Agents[agent.Guid] = &agent
client := models.Client{ client := models.Client{
Guid: models.ClientGuid(strconv.Itoa(rand.Int())), Guid: models.ClientGuid(strconv.Itoa(rand.Int())),
RemoteAddr: models.RemoteAddr("10.1.3.3"), RemoteAddr: models.RemoteAddr("10.1.3.3"),
@ -104,7 +104,9 @@ func main() {
StartTime: time.Now().In(japan), StartTime: time.Now().In(japan),
SessionType: models.SessionType("sftp"), SessionType: models.SessionType("sftp"),
} }
state.Clients.Put(client.Guid, &client) state.Clients[client.Guid] = &client
return templates2.SessionsTab(state, netherlands)
agents, clients := state.Slices()
return templates2.SessionsTab(agents, clients, netherlands)
}) })
} }

View File

@ -2,7 +2,7 @@ package models
import ( import (
"git.wamblee.org/converge/pkg/comms" "git.wamblee.org/converge/pkg/comms"
"git.wamblee.org/converge/pkg/support/collections" "sort"
"sync/atomic" "sync/atomic"
"time" "time"
) )
@ -56,25 +56,43 @@ type Client struct {
// Created by the server and used for updating the web client // Created by the server and used for updating the web client
// and prometheus metrics. // and prometheus metrics.
type State struct { type State struct {
Agents *collections.LinkedMap[AgentGuid, *Agent] Agents map[AgentGuid]*Agent
Clients *collections.LinkedMap[ClientGuid, *Client] Clients map[ClientGuid]*Client
} }
func NewState() *State { func NewState() *State {
return &State{ return &State{
Agents: collections.NewLinkedMap[AgentGuid, *Agent](), Agents: make(map[AgentGuid]*Agent),
Clients: collections.NewLinkedMap[ClientGuid, *Client](), Clients: make(map[ClientGuid]*Client),
} }
} }
// for copy on write // for copy on write
func (state *State) Copy() *State { func (state *State) Copy() *State {
res := NewState() res := NewState()
for entry := range state.Agents.RangeEntries() { for k, v := range state.Agents {
res.Agents.Put(entry.Key, entry.Value) res.Agents[k] = v
} }
for entry := range state.Clients.RangeEntries() { for k, v := range state.Clients {
res.Clients.Put(entry.Key, entry.Value) res.Clients[k] = v
} }
return res return res
} }
func (state *State) Slices() ([]*Agent, []*Client) {
agents := make([]*Agent, 0, len(state.Agents))
clients := make([]*Client, 0, len(state.Clients))
for _, agent := range state.Agents {
agents = append(agents, agent)
}
for _, client := range state.Clients {
clients = append(clients, client)
}
sort.Slice(agents, func(i int, j int) bool {
return agents[i].StartTime.Before(agents[j].StartTime)
})
sort.Slice(clients, func(i int, j int) bool {
return clients[i].StartTime.Before(clients[j].StartTime)
})
return agents, clients
}

View File

@ -140,7 +140,7 @@ func (admin *Admin) AddAgent(publicId models.RendezVousId, agentInfo comms.Envir
agent := newAgent(commChannel, publicId, agentInfo) agent := newAgent(commChannel, publicId, agentInfo)
admin.state = admin.state.Copy() admin.state = admin.state.Copy()
admin.state.Agents.Put(agent.Info.Guid, agent.Info) admin.state.Agents[agent.Info.Guid] = agent.Info
admin.agents[publicId] = agent admin.agents[publicId] = agent
return agent, nil return agent, nil
} }
@ -175,7 +175,7 @@ func (admin *Admin) AddClient(publicId models.RendezVousId, clientConn iowrapper
} }
admin.state = admin.state.Copy() admin.state = admin.state.Copy()
admin.state.Clients.Put(client.Info.Guid, client.Info) admin.state.Clients[client.Info.Guid] = client.Info
admin.clients[client.Info.ClientId] = client admin.clients[client.Info.ClientId] = client
return client, nil return client, nil
} }
@ -206,7 +206,7 @@ func (admin *Admin) RemoveAgent(publicId models.RendezVousId) error {
log.Printf("Could not close yamux client session for '%s'\n", publicId) log.Printf("Could not close yamux client session for '%s'\n", publicId)
} }
admin.state = admin.state.Copy() admin.state = admin.state.Copy()
admin.state.Agents.Delete(agent.Info.Guid) delete(admin.state.Agents, agent.Info.Guid)
delete(admin.agents, publicId) delete(admin.agents, publicId)
return nil return nil
} }
@ -222,7 +222,7 @@ func (admin *Admin) RemoveClient(client *clientConnection) error {
_ = client.clientConnection.Close() _ = client.clientConnection.Close()
admin.state = admin.state.Copy() admin.state = admin.state.Copy()
admin.state.Clients.Delete(client.Info.Guid) delete(admin.state.Clients, client.Info.Guid)
delete(admin.clients, client.Info.ClientId) delete(admin.clients, client.Info.ClientId)
return nil return nil
} }
@ -230,7 +230,7 @@ func (admin *Admin) RemoveClient(client *clientConnection) error {
func (admin *Admin) SetSessionType(clientId models.ClientId, sessionType models.SessionType) { func (admin *Admin) SetSessionType(clientId models.ClientId, sessionType models.SessionType) {
admin.mutex.Lock() admin.mutex.Lock()
defer admin.mutex.Unlock() defer admin.mutex.Unlock()
for client := range admin.state.Clients.RangeValues() { for _, client := range admin.state.Clients {
if client.ClientId == clientId { if client.ClientId == clientId {
client.SessionType = sessionType client.SessionType = sessionType
break break

View File

@ -134,7 +134,7 @@ func logStatusImpl(admin *models.State, notifier Notifier) {
lines = append(lines, fmt.Sprintf(format, "AGENT", "ACTIVE_SINCE", "EXPIRY_TIME", lines = append(lines, fmt.Sprintf(format, "AGENT", "ACTIVE_SINCE", "EXPIRY_TIME",
"USER", "HOST", "OS")) "USER", "HOST", "OS"))
for agent := range admin.Agents.RangeValues() { for _, agent := range admin.Agents {
lines = append(lines, fmt.Sprintf(format, agent.PublicId, lines = append(lines, fmt.Sprintf(format, agent.PublicId,
agent.StartTime.Format(time.DateTime), agent.StartTime.Format(time.DateTime),
agent.GetExpiryTime().Format(time.DateTime), agent.GetExpiryTime().Format(time.DateTime),
@ -145,7 +145,7 @@ func logStatusImpl(admin *models.State, notifier Notifier) {
lines = append(lines, "") lines = append(lines, "")
format = "%-10s %-20s %-20s %-20s %-20s" format = "%-10s %-20s %-20s %-20s %-20s"
lines = append(lines, fmt.Sprintf(format, "CLIENT", "AGENT", "ACTIVE_SINCE", "REMOTE_ADDRESS", "SESSION_TYPE")) lines = append(lines, fmt.Sprintf(format, "CLIENT", "AGENT", "ACTIVE_SINCE", "REMOTE_ADDRESS", "SESSION_TYPE"))
for client := range admin.Clients.RangeValues() { for _, client := range admin.Clients {
lines = append(lines, fmt.Sprintf(format, lines = append(lines, fmt.Sprintf(format,
client.ClientId, client.ClientId,
client.PublicId, client.PublicId,

View File

@ -137,7 +137,8 @@ func (session *WebSession) WriteNotifications(location *time.Location, ctx conte
} }
func (session *WebSession) writeNotificationToClient(location *time.Location, notification *models.State) bool { func (session *WebSession) writeNotificationToClient(location *time.Location, notification *models.State) bool {
err := templates.State(notification, location).Render(context.Background(), session.conn) agents, clients := notification.Slices()
err := templates.State(agents, clients, location).Render(context.Background(), session.conn)
if err != nil { if err != nil {
log.Printf("WS connection closed: %v", err) log.Printf("WS connection closed: %v", err)
return false return false

View File

@ -7,7 +7,7 @@ import (
) )
templ Sessions(state *models.State, loc *time.Location) { templ Sessions(agents []*models.Agent, clients []*models.Client, loc *time.Location) {
<div> <div>
<div ws-send <div ws-send
hx-trigger="load" hx-trigger="load"
@ -16,8 +16,8 @@ templ Sessions(state *models.State, loc *time.Location) {
<h1>sessions</h1> <h1>sessions</h1>
<div id="status"> <div id="status">
if state != nil { if len(agents) > 0 {
@State(state, loc) @State(agents, clients, loc)
} else { } else {
Loading... Loading...
} }
@ -26,12 +26,12 @@ templ Sessions(state *models.State, loc *time.Location) {
} }
templ State(state *models.State, location *time.Location) { templ State(agents []*models.Agent, clients []*models.Client, location *time.Location) {
<div id="status"> <div id="status">
<h3>agents</h3> <h3>agents</h3>
if state.Agents.Len() == 0 { if len(agents) == 0 {
<p>-</p> <p>-</p>
} else { } else {
<table class="table"> <table class="table">
@ -46,7 +46,7 @@ templ State(state *models.State, location *time.Location) {
<th>shell</th> <th>shell</th>
</tr> </tr>
</thead> </thead>
for agent := range state.Agents.RangeValues() { for _, agent := range agents {
<tr> <tr>
<td>{string(agent.PublicId)}</td> <td>{string(agent.PublicId)}</td>
<td>{agent.StartTime.In(location).Format(time.DateTime)}</td> <td>{agent.StartTime.In(location).Format(time.DateTime)}</td>
@ -64,7 +64,7 @@ templ State(state *models.State, location *time.Location) {
<h3>clients</h3> <h3>clients</h3>
if state.Clients.Len() == 0 { if len(clients) == 0 {
<p>-</p> <p>-</p>
} else { } else {
<table class="table"> <table class="table">
@ -80,7 +80,7 @@ templ State(state *models.State, location *time.Location) {
<th>shell</th> <th>shell</th>
</tr> </tr>
</thead> </thead>
for client := range state.Clients.RangeValues() { for _, client := range clients {
<tr> <tr>
<td>{string(client.ClientId)}</td> <td>{string(client.ClientId)}</td>
<td>{client.StartTime.In(location).Format(time.DateTime)}</td> <td>{client.StartTime.In(location).Format(time.DateTime)}</td>
@ -98,8 +98,8 @@ templ State(state *models.State, location *time.Location) {
} }
templ SessionsTab(state *models.State, loc *time.Location) { templ SessionsTab(agents []*models.Agent, clients []*models.Client, loc *time.Location) {
@BasePage(4) { @BasePage(4) {
@Sessions(state, loc) @Sessions(agents, clients, loc)
} }
} }