introdcution of typesafe ids.

This commit is contained in:
Erik Brakkee 2024-08-15 18:32:59 +02:00
parent c86ea894d1
commit 556315906d
9 changed files with 100 additions and 97 deletions

View File

@ -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) {

View File

@ -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() {

View File

@ -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)

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -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 {

View File

@ -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)

View File

@ -48,7 +48,7 @@ templ State(state *models.State, location *time.Location) {
</thead>
for _, agent := range state.Agents {
<tr>
<td>{agent.PublicId}</td>
<td>{string(agent.PublicId)}</td>
<td>{agent.StartTime.In(location).Format(time.DateTime)}</td>
<td>{agent.ExpiryTime.In(location).Format(time.DateTime)}</td>
<td>{agent.EnvironmentInfo.Username}</td>
@ -82,10 +82,10 @@ templ State(state *models.State, location *time.Location) {
</thead>
for _, client := range state.Clients {
<tr>
<td>{client.ClientId}</td>
<td>{string(client.ClientId)}</td>
<td>{client.StartTime.In(location).Format(time.DateTime)}</td>
<td>{client.SessionType}</td>
<td>{client.PublicId}</td>
<td>{string(client.SessionType)}</td>
<td>{string(client.PublicId)}</td>
<td>{client.EnvironmentInfo.Username}</td>
<td>{client.EnvironmentInfo.Hostname}</td>
<td>{client.EnvironmentInfo.OS}</td>