From 556315906d918c0d4d267de062a8edbc9479a943 Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Thu, 15 Aug 2024 18:32:59 +0200 Subject: [PATCH] introdcution of typesafe ids. --- cmd/converge/converge.go | 5 +-- cmd/converge/prometheus.go | 53 +++++++++++++++-------------- cmd/templaterender/render.go | 14 ++++---- pkg/models/agent.go | 18 ---------- pkg/models/client.go | 16 --------- pkg/models/state.go | 35 +++++++++++++++++++ pkg/server/admin/admin.go | 42 +++++++++++------------ pkg/server/matchmaker/matchmaker.go | 6 ++-- pkg/server/templates/sessions.templ | 8 ++--- 9 files changed, 100 insertions(+), 97 deletions(-) delete mode 100644 pkg/models/agent.go diff --git a/cmd/converge/converge.go b/cmd/converge/converge.go index aed35f4..3f37074 100644 --- a/cmd/converge/converge.go +++ b/cmd/converge/converge.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "git.wamblee.org/converge/pkg/models" "git.wamblee.org/converge/pkg/server/matchmaker" "git.wamblee.org/converge/pkg/support/websocketutil" "log" @@ -15,13 +16,13 @@ import ( _ "time/tzdata" ) -func parsePublicId(path string) (publicId string, _ error) { +func parsePublicId(path string) (publicId models.RendezVousId, _ error) { pattern := regexp.MustCompile("/([^/]+)$") matches := pattern.FindStringSubmatch(path) if len(matches) != 2 { return "", fmt.Errorf("Invalid URL path '%s'", path) } - return matches[1], nil + return models.RendezVousId(matches[1]), nil } func catchAllHandler(contextPath string) func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/converge/prometheus.go b/cmd/converge/prometheus.go index 9c355e9..f669e31 100644 --- a/cmd/converge/prometheus.go +++ b/cmd/converge/prometheus.go @@ -14,14 +14,14 @@ const NAMESPACE = "converge" // more efficient state representation for state type PrometheusState struct { - agents map[string]*models.Agent - clients map[string]*models.Client + agents map[models.AgentGuid]*models.Agent + clients map[models.ClientGuid]*models.Client } func NewPrometheusState(state *models.State) *PrometheusState { res := PrometheusState{ - agents: make(map[string]*models.Agent), - clients: make(map[string]*models.Client), + agents: make(map[models.AgentGuid]*models.Agent), + clients: make(map[models.ClientGuid]*models.Client), } for i, _ := range state.Agents { res.agents[state.Agents[i].Guid] = &state.Agents[i] @@ -121,9 +121,9 @@ var ( func agentLabels(agent *models.Agent) prometheus.Labels { return prometheus.Labels{ - "agent_guid": agent.Guid, - "agent_address": agent.RemoteAddr, - "agent_id": agent.PublicId, + "agent_guid": string(agent.Guid), + "agent_address": string(agent.RemoteAddr), + "agent_id": string(agent.PublicId), "agent_username": agent.EnvironmentInfo.Username, "agent_hostname": agent.EnvironmentInfo.Hostname, "agent_pwd": agent.EnvironmentInfo.Pwd, @@ -134,12 +134,12 @@ func agentLabels(agent *models.Agent) prometheus.Labels { func clientLabels(client *models.Client) prometheus.Labels { return prometheus.Labels{ - "client_guid": client.Guid, - "client_address": client.RemoteAddr, - "client_id": client.ClientId, - "agent_id": client.PublicId, - "agent_guid": client.AgentGuid, - "client_sessiontype": client.SessionType, + "client_guid": string(client.Guid), + "client_address": string(client.RemoteAddr), + "client_id": string(client.ClientId), + "agent_id": string(client.PublicId), + "agent_guid": string(client.AgentGuid), + "client_sessiontype": string(client.SessionType), "client_username": client.EnvironmentInfo.Username, "client_hostname": client.EnvironmentInfo.Hostname, "client_pwd": client.EnvironmentInfo.Pwd, @@ -154,11 +154,12 @@ func agentActive(agent *models.Agent) { removeAgentInfoMetrics(prevAgent) } agentInfo.With(agentLabels(agent)).Set(1) + agentGuid := string(agent.Guid) agentStartTime. - With(prometheus.Labels{"agent_guid": agent.Guid}). + With(prometheus.Labels{"agent_guid": agentGuid}). Set(float64(agent.StartTime.UnixMilli())) agentDuration. - With(prometheus.Labels{"agent_guid": agent.Guid}). + With(prometheus.Labels{"agent_guid": agentGuid}). Set(float64(time.Now().Sub(agent.StartTime).Seconds())) } @@ -168,12 +169,12 @@ func clientActive(client *models.Client) { removeClientInfoMetrics(prevClient) } clientInfo.With(clientLabels(client)).Set(1) - + clientGuid := string(client.Guid) clientStartTime. - With(prometheus.Labels{"client_guid": client.Guid}). + With(prometheus.Labels{"client_guid": clientGuid}). Set(float64(client.StartTime.UnixMilli())) clientDuration. - With(prometheus.Labels{"client_guid": client.Guid}). + With(prometheus.Labels{"client_guid": clientGuid}). Set(float64(time.Now().Sub(client.StartTime).Seconds())) } @@ -223,23 +224,23 @@ func updateMetrics(state *models.State) { func updateDurations() { for _, agent := range lastState.agents { agentDuration. - With(prometheus.Labels{"agent_guid": agent.Guid}). + With(prometheus.Labels{"agent_guid": string(agent.Guid)}). Set(float64(time.Now().Sub(agent.StartTime).Seconds())) } for _, client := range lastState.clients { clientDuration. - With(prometheus.Labels{"client_guid": client.Guid}). + With(prometheus.Labels{"client_guid": string(client.Guid)}). Set(float64(time.Now().Sub(client.StartTime).Seconds())) } } func updateMetricsImpl(state *PrometheusState) { - agentGuids := make(map[string]*models.Agent) - clientGuids := make(map[string]*models.Client) + agentGuids := make(map[models.AgentGuid]*models.Agent) + clientGuids := make(map[models.ClientGuid]*models.Client) agentCount.Set(float64(len(state.agents))) - disconnectedAgents := make(map[string]*models.Agent) + disconnectedAgents := make(map[models.AgentGuid]*models.Agent) for _, agent := range lastState.agents { disconnectedAgents[agent.Guid] = agent } @@ -258,7 +259,7 @@ func updateMetricsImpl(state *PrometheusState) { clientCount.Set(float64(len(state.clients))) // with this app - disconnectedClients := make(map[string]*models.Client) + disconnectedClients := make(map[models.ClientGuid]*models.Client) for _, client := range lastState.clients { disconnectedClients[client.Guid] = client } @@ -283,7 +284,7 @@ func removeAgentInfoMetrics(agent *models.Agent) bool { func removeAgentMetrics(agent *models.Agent) { ok1 := removeAgentInfoMetrics(agent) - guidLabels := prometheus.Labels{"agent_guid": agent.Guid} + guidLabels := prometheus.Labels{"agent_guid": string(agent.Guid)} ok2 := agentStartTime.Delete(guidLabels) // delayed deletion of the duration sow we are sure the prometheus has the last data. go func() { @@ -305,7 +306,7 @@ func removeClientInfoMetrics(client *models.Client) bool { func removeClientMetrics(client *models.Client) { ok1 := removeClientInfoMetrics(client) - guidLabels := prometheus.Labels{"client_guid": client.Guid} + guidLabels := prometheus.Labels{"client_guid": string(client.Guid)} ok2 := clientStartTime.Delete(guidLabels) // delayed deletion of the duration sow we are sure the prometheus has the last data. go func() { diff --git a/cmd/templaterender/render.go b/cmd/templaterender/render.go index c69dd1d..51770a1 100644 --- a/cmd/templaterender/render.go +++ b/cmd/templaterender/render.go @@ -81,7 +81,7 @@ func main() { state := models.State{} agent := models.Agent{ - Guid: strconv.Itoa(rand.Int()), + Guid: models.AgentGuid(strconv.Itoa(rand.Int())), RemoteAddr: "10.220.1.3:3333", PublicId: "id", StartTime: time.Now().In(japan), @@ -96,13 +96,13 @@ func main() { } state.Agents = append(state.Agents, agent) client := models.Client{ - Guid: strconv.Itoa(rand.Int()), - RemoteAddr: "10.1.3.3", - PublicId: "c1", - AgentGuid: "12342342", - ClientId: "3", + Guid: models.ClientGuid(strconv.Itoa(rand.Int())), + RemoteAddr: models.RemoteAddr("10.1.3.3"), + PublicId: models.RendezVousId("c1"), + AgentGuid: models.AgentGuid("12342342"), + ClientId: models.ClientId("3"), StartTime: time.Now().In(japan), - SessionType: "sftp", + SessionType: models.SessionType("sftp"), } state.Clients = append(state.Clients, client) return templates2.SessionsTab(&state, netherlands) diff --git a/pkg/models/agent.go b/pkg/models/agent.go deleted file mode 100644 index 2d3b54c..0000000 --- a/pkg/models/agent.go +++ /dev/null @@ -1,18 +0,0 @@ -package models - -import ( - "git.wamblee.org/converge/pkg/comms" - "time" -) - -type Agent struct { - Guid string - RemoteAddr string - PublicId string - StartTime time.Time - - // TODO add remote address. - - EnvironmentInfo comms.EnvironmentInfo - ExpiryTime time.Time -} diff --git a/pkg/models/client.go b/pkg/models/client.go index 436e303..2640e7f 100644 --- a/pkg/models/client.go +++ b/pkg/models/client.go @@ -1,17 +1 @@ package models - -import ( - "git.wamblee.org/converge/pkg/comms" - "time" -) - -type Client struct { - Guid string - RemoteAddr string - PublicId string - ClientId string - AgentGuid string - StartTime time.Time - SessionType string - EnvironmentInfo comms.EnvironmentInfo -} diff --git a/pkg/models/state.go b/pkg/models/state.go index 74044fc..1c1dc23 100644 --- a/pkg/models/state.go +++ b/pkg/models/state.go @@ -1,5 +1,40 @@ package models +import ( + "git.wamblee.org/converge/pkg/comms" + "time" +) + +type RendezVousId string +type AgentGuid string +type ClientGuid string +type ClientId string +type SessionType string +type RemoteAddr string + +type Agent struct { + Guid AgentGuid + RemoteAddr RemoteAddr + PublicId RendezVousId + StartTime time.Time + + // TODO add remote address. + + EnvironmentInfo comms.EnvironmentInfo + ExpiryTime time.Time +} + +type Client struct { + Guid ClientGuid + RemoteAddr RemoteAddr + PublicId RendezVousId + ClientId ClientId + AgentGuid AgentGuid + StartTime time.Time + SessionType SessionType + EnvironmentInfo comms.EnvironmentInfo +} + // State is a description of the current state of converge. // Created by the server and used for updating the web client // and prometheus metrics. diff --git a/pkg/server/admin/admin.go b/pkg/server/admin/admin.go index 1f69957..3b4c286 100644 --- a/pkg/server/admin/admin.go +++ b/pkg/server/admin/admin.go @@ -30,11 +30,11 @@ type ClientConnection struct { clientConnection iowrappers2.ReadWriteAddrCloser } -func newAgent(commChannel comms.CommChannel, publicId string, agentInfo comms.EnvironmentInfo) *agentConnection { +func newAgent(commChannel comms.CommChannel, publicId models.RendezVousId, agentInfo comms.EnvironmentInfo) *agentConnection { return &agentConnection{ Agent: models.Agent{ - Guid: strconv.Itoa(rand.Int()), - RemoteAddr: commChannel.Session.RemoteAddr().String(), + Guid: models.AgentGuid(strconv.Itoa(rand.Int())), + RemoteAddr: models.RemoteAddr(commChannel.Session.RemoteAddr().String()), PublicId: publicId, StartTime: time.Now(), EnvironmentInfo: agentInfo, @@ -43,15 +43,15 @@ func newAgent(commChannel comms.CommChannel, publicId string, agentInfo comms.En } } -func newClient(publicId string, clientConn iowrappers2.ReadWriteAddrCloser, - agentConn net.Conn, agentGuid string) *ClientConnection { +func newClient(publicId models.RendezVousId, clientConn iowrappers2.ReadWriteAddrCloser, + agentConn net.Conn, agentGuid models.AgentGuid) *ClientConnection { return &ClientConnection{ Client: models.Client{ - Guid: strconv.Itoa(rand.Int()), - RemoteAddr: clientConn.RemoteAddr().String(), + Guid: models.ClientGuid(strconv.Itoa(rand.Int())), + RemoteAddr: models.RemoteAddr(clientConn.RemoteAddr().String()), PublicId: publicId, AgentGuid: agentGuid, - ClientId: strconv.Itoa(clientIdGenerator.IncrementAndGet()), + ClientId: models.ClientId(strconv.Itoa(clientIdGenerator.IncrementAndGet())), StartTime: time.Now(), }, agentConnection: agentConn, @@ -66,14 +66,14 @@ func (match *ClientConnection) Synchronize() { type Admin struct { // map of public id to agent mutex sync.Mutex - agents map[string]*agentConnection + agents map[models.RendezVousId]*agentConnection clients []*ClientConnection } func NewAdmin() *Admin { return &Admin{ mutex: sync.Mutex{}, - agents: make(map[string]*agentConnection), + agents: make(map[models.RendezVousId]*agentConnection), clients: make([]*ClientConnection, 0), // not strictly needed } } @@ -93,8 +93,8 @@ func (admin *Admin) CreateNotifification() *models.State { return &state } -func (admin *Admin) getFreeId(publicId string) (string, error) { - usedIds := make(map[string]bool) +func (admin *Admin) getFreeId(publicId models.RendezVousId) (models.RendezVousId, error) { + usedIds := make(map[models.RendezVousId]bool) for _, agent := range admin.agents { usedIds[agent.PublicId] = true } @@ -103,16 +103,16 @@ func (admin *Admin) getFreeId(publicId string) (string, error) { } if usedIds[publicId] { for i := 0; i < 100; i++ { - candidate := publicId + "-" + strconv.Itoa(i) - if !usedIds[candidate] { - return candidate, nil + candidate := string(publicId) + "-" + strconv.Itoa(i) + if !usedIds[models.RendezVousId(candidate)] { + return models.RendezVousId(candidate), nil } } } return "", fmt.Errorf("Could not allocate agent id based on requested public id '%s'", publicId) } -func (admin *Admin) AddAgent(publicId string, agentInfo comms.EnvironmentInfo, conn io.ReadWriteCloser) (*agentConnection, error) { +func (admin *Admin) AddAgent(publicId models.RendezVousId, agentInfo comms.EnvironmentInfo, conn io.ReadWriteCloser) (*agentConnection, error) { admin.mutex.Lock() defer admin.mutex.Unlock() @@ -126,7 +126,7 @@ func (admin *Admin) AddAgent(publicId string, agentInfo comms.EnvironmentInfo, c comms.SendRegistrationMessage(conn, comms.AgentRegistration{ Ok: true, Message: message, - Id: publicId, + Id: string(publicId), }) } else { comms.SendRegistrationMessage(conn, comms.AgentRegistration{ @@ -149,7 +149,7 @@ func (admin *Admin) AddAgent(publicId string, agentInfo comms.EnvironmentInfo, c return agent, nil } -func (admin *Admin) AddClient(publicId string, clientConn iowrappers2.ReadWriteAddrCloser) (*ClientConnection, error) { +func (admin *Admin) AddClient(publicId models.RendezVousId, clientConn iowrappers2.ReadWriteAddrCloser) (*ClientConnection, error) { admin.mutex.Lock() defer admin.mutex.Unlock() @@ -172,7 +172,7 @@ func (admin *Admin) AddClient(publicId string, clientConn iowrappers2.ReadWriteA // Before using this connection for SSH we use it to send client metadata to the // agent err = comms.SendClientInfo(agentConn, comms.ClientInfo{ - ClientId: client.ClientId, + ClientId: string(client.ClientId), }) if err != nil { return nil, err @@ -194,7 +194,7 @@ func (admin *Admin) getAgentConnection(agent *agentConnection) (net.Conn, error) return agentConn, err } -func (admin *Admin) RemoveAgent(publicId string) error { +func (admin *Admin) RemoveAgent(publicId models.RendezVousId) error { admin.mutex.Lock() defer admin.mutex.Unlock() @@ -230,7 +230,7 @@ func (admin *Admin) RemoveClient(client *ClientConnection) error { return nil } -func (admin *Admin) SetSessionType(clientId string, sessionType string) { +func (admin *Admin) SetSessionType(clientId models.ClientId, sessionType models.SessionType) { admin.mutex.Lock() defer admin.mutex.Unlock() for _, client := range admin.clients { diff --git a/pkg/server/matchmaker/matchmaker.go b/pkg/server/matchmaker/matchmaker.go index 39a82e0..94ed7a0 100644 --- a/pkg/server/matchmaker/matchmaker.go +++ b/pkg/server/matchmaker/matchmaker.go @@ -25,7 +25,7 @@ func NewMatchMaker(notifier Notifier) *MatchMaker { return &converge } -func (converge *MatchMaker) Register(publicId string, conn io.ReadWriteCloser) error { +func (converge *MatchMaker) Register(publicId models.RendezVousId, conn io.ReadWriteCloser) error { serverInfo := comms.ServerInfo{} @@ -53,7 +53,7 @@ func (converge *MatchMaker) Register(publicId string, conn io.ReadWriteCloser) e }, func(session comms.SessionInfo) { log.Println("Recceived sessioninfo ", session) - converge.admin.SetSessionType(session.ClientId, session.SessionType) + converge.admin.SetSessionType(models.ClientId(session.ClientId), models.SessionType(session.SessionType)) }, func(expiry comms.ExpiryTimeUpdate) { agent.ExpiryTime = expiry.ExpiryTime @@ -68,7 +68,7 @@ func (converge *MatchMaker) Register(publicId string, conn io.ReadWriteCloser) e return nil } -func (converge *MatchMaker) Connect(wsProxyMode bool, publicId string, conn iowrappers2.ReadWriteAddrCloser) error { +func (converge *MatchMaker) Connect(wsProxyMode bool, publicId models.RendezVousId, conn iowrappers2.ReadWriteAddrCloser) error { defer conn.Close() log.Printf("Using wsproxy protocol %v", wsProxyMode) diff --git a/pkg/server/templates/sessions.templ b/pkg/server/templates/sessions.templ index 17a099e..4a17096 100644 --- a/pkg/server/templates/sessions.templ +++ b/pkg/server/templates/sessions.templ @@ -48,7 +48,7 @@ templ State(state *models.State, location *time.Location) { for _, agent := range state.Agents { - {agent.PublicId} + {string(agent.PublicId)} {agent.StartTime.In(location).Format(time.DateTime)} {agent.ExpiryTime.In(location).Format(time.DateTime)} {agent.EnvironmentInfo.Username} @@ -82,10 +82,10 @@ templ State(state *models.State, location *time.Location) { for _, client := range state.Clients { - {client.ClientId} + {string(client.ClientId)} {client.StartTime.In(location).Format(time.DateTime)} - {client.SessionType} - {client.PublicId} + {string(client.SessionType)} + {string(client.PublicId)} {client.EnvironmentInfo.Username} {client.EnvironmentInfo.Hostname} {client.EnvironmentInfo.OS}