Files
threshold/internal/daemon/cluster/downstreammanager.go
T
Todd 1e89be1b1a Downstream worker connections are tracked by worker id (#2949)
When possible downstream workers will include their worker id information in
the connections they establish with the upstream using the nodeenrollment
library.  The tracking listener now tracks connections that are connected in
this same way instead of just connections of type *tls.Conn.
2023-02-10 15:27:42 -08:00

156 lines
4.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package cluster
import (
"net"
"sync"
)
// DownstreamManager associates downstream worker key identifiers with the
// connections to a specific server.
// It is safe to access DownstreamManager concurrently.
type DownstreamManager struct {
keyToWorkerId map[string]string
workerIdToKeyIds map[string]map[string]struct{}
// maps a key id to a connection initiated by a downstream worker
workerConnections map[string][]net.Conn
l sync.RWMutex
}
func NewDownstreamManager() *DownstreamManager {
return &DownstreamManager{
workerConnections: make(map[string][]net.Conn),
keyToWorkerId: make(map[string]string),
workerIdToKeyIds: make(map[string]map[string]struct{}),
}
}
func (m *DownstreamManager) mapKeyToWorkerId(keyId, wId string) {
m.l.Lock()
defer m.l.Unlock()
m.keyToWorkerId[keyId] = wId
if _, ok := m.workerIdToKeyIds[wId]; !ok {
m.workerIdToKeyIds[wId] = make(map[string]struct{})
}
m.workerIdToKeyIds[wId][keyId] = struct{}{}
}
// addConnection adds a connection associated with the provided downstream
// worker key identifier.
func (m *DownstreamManager) addConnection(id string, c net.Conn) {
m.l.Lock()
defer m.l.Unlock()
m.workerConnections[id] = append(m.workerConnections[id], c)
}
// disconnectWorkerId closes all connections associated with the provided
// worker public id
func (m *DownstreamManager) disconnectWorkerId(id string) {
m.l.Lock()
defer m.l.Unlock()
for k := range m.workerIdToKeyIds[id] {
for _, c := range m.workerConnections[k] {
c.Close()
}
delete(m.workerConnections, k)
delete(m.keyToWorkerId, k)
}
delete(m.workerIdToKeyIds, id)
}
// disconnectKeys closes all connections associated with the provided worker key
// identifier.
func (m *DownstreamManager) disconnectKeys(id string) {
m.l.Lock()
defer m.l.Unlock()
for _, c := range m.workerConnections[id] {
c.Close()
}
delete(m.workerConnections, id)
// clean up all the associations between worker id and key ids
if wId, ok := m.keyToWorkerId[id]; ok {
delete(m.keyToWorkerId, id)
delete(m.workerIdToKeyIds[wId], id)
if len(m.workerIdToKeyIds[wId]) == 0 {
delete(m.workerIdToKeyIds, wId)
}
}
}
// Connected returns a connected state which provides the worker ids that are
// being tracked and any key ids for which we don't know the worker id.
func (m *DownstreamManager) Connected() *connectedState {
m.l.RLock()
defer m.l.RUnlock()
seenWIds := make(map[string]struct{})
var workerIds, keyIds []string
for k, v := range m.workerConnections {
if len(v) > 0 {
if w, ok := m.keyToWorkerId[k]; ok {
if _, ok := seenWIds[w]; !ok {
seenWIds[w] = struct{}{}
workerIds = append(workerIds, w)
}
} else {
keyIds = append(keyIds, k)
}
}
}
return &connectedState{
dm: m,
unmappedKeyIds: keyIds,
workerIds: workerIds,
}
}
// connectedState provides a state of the downstream manager and allows users
// to request connections be disconnected that are capturred in this state.
type connectedState struct {
dm *DownstreamManager
unmappedKeyIds []string
workerIds []string
}
// UnmappedKeyIds are the key ids for which no worker id association is known
// and which are tracking at least 1 connection.
func (s *connectedState) UnmappedKeyIds() []string {
return s.unmappedKeyIds
}
// WorkerIds are the public ids for workers which we are tracking connections.
func (s *connectedState) WorkerIds() []string {
return s.workerIds
}
// DisconnectMissingWorkers disconnects all workers that are not in the slice
// of worker ids provided but are tracked in this connected state.
func (s *connectedState) DisconnectMissingWorkers(workers []string) {
aw := make(map[string]struct{}, len(workers))
for _, i := range workers {
aw[i] = struct{}{}
}
for _, w := range s.workerIds {
if _, found := aw[w]; !found {
s.dm.disconnectWorkerId(w)
}
}
}
// DisconnectMissingKeyIds disconnects all workers which are not in the slice of
// key ids provided but are tracked in this connected state.
func (s *connectedState) DisconnectMissingKeyIds(keyIds []string) {
am := make(map[string]struct{}, len(keyIds))
for _, i := range keyIds {
am[i] = struct{}{}
}
for _, i := range s.unmappedKeyIds {
if _, found := am[i]; !found {
s.dm.disconnectKeys(i)
}
}
}