package servicemap import ( "sync" "github.com/up9inc/mizu/shared" "github.com/up9inc/mizu/shared/logger" tapApi "github.com/up9inc/mizu/tap/api" ) const ( ServiceMapEnabled = "enabled" ServiceMapDisabled = "disabled" UnresolvedNodeName = "unresolved" ) var instance *serviceMap var once sync.Once func GetInstance() ServiceMap { once.Do(func() { instance = newServiceMap() logger.Log.Debug("Service Map Initialized") }) return instance } type serviceMap struct { config *shared.MizuAgentConfig graph *graph entriesProcessed int } type ServiceMap interface { SetConfig(config *shared.MizuAgentConfig) IsEnabled() bool NewTCPEntry(source *tapApi.TCP, destination *tapApi.TCP, protocol *tapApi.Protocol) GetStatus() ServiceMapStatus GetNodes() []ServiceMapNode GetEdges() []ServiceMapEdge GetEntriesProcessedCount() int GetNodesCount() int GetEdgesCount() int Reset() } func newServiceMap() *serviceMap { return &serviceMap{ config: nil, entriesProcessed: 0, graph: newDirectedGraph(), } } type key string type entryData struct { key key entry *tapApi.TCP } type nodeData struct { id int entry *tapApi.TCP count int } type edgeProtocol struct { protocol *tapApi.Protocol count int } type edgeData struct { data map[key]*edgeProtocol } type graph struct { Nodes map[key]*nodeData Edges map[key]map[key]*edgeData } func newDirectedGraph() *graph { return &graph{ Nodes: make(map[key]*nodeData), Edges: make(map[key]map[key]*edgeData), } } func newNodeData(id int, e *tapApi.TCP) *nodeData { return &nodeData{ id: id, entry: e, count: 1, } } func newEdgeData(p *tapApi.Protocol) *edgeData { return &edgeData{ data: map[key]*edgeProtocol{ key(p.Name): { protocol: p, count: 1, }, }, } } func (s *serviceMap) nodeExists(k key) (*nodeData, bool) { n, ok := s.graph.Nodes[k] return n, ok } func (s *serviceMap) addNode(k key, e *tapApi.TCP) (*nodeData, bool) { nd, exists := s.nodeExists(k) if !exists { s.graph.Nodes[k] = newNodeData(len(s.graph.Nodes)+1, e) return s.graph.Nodes[k], true } return nd, false } func (s *serviceMap) addEdge(u, v *entryData, p *tapApi.Protocol) { if n, ok := s.addNode(u.key, u.entry); !ok { n.count++ } if n, ok := s.addNode(v.key, v.entry); !ok { n.count++ } if _, ok := s.graph.Edges[u.key]; !ok { s.graph.Edges[u.key] = make(map[key]*edgeData) } // new edge u -> v pair // protocol is the same for u and v if e, ok := s.graph.Edges[u.key][v.key]; ok { // edge data already exists for u -> v pair // we have a new protocol for this u -> v pair k := key(p.Name) if pd, pOk := e.data[k]; pOk { // protocol key already exists, just increment the count pd.count++ } else { // new protocol key e.data[k] = &edgeProtocol{ protocol: p, count: 1, } } } else { // new edge data for u -> v pair s.graph.Edges[u.key][v.key] = newEdgeData(p) } s.entriesProcessed++ } func (s *serviceMap) SetConfig(config *shared.MizuAgentConfig) { s.config = config } func (s *serviceMap) IsEnabled() bool { if s.config != nil && s.config.ServiceMap { return true } return false } func (s *serviceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Protocol) { if !s.IsEnabled() { return } srcEntry := &entryData{ key: key(src.IP), entry: src, } if len(srcEntry.entry.Name) == 0 { srcEntry.entry.Name = UnresolvedNodeName } dstEntry := &entryData{ key: key(dst.IP), entry: dst, } if len(dstEntry.entry.Name) == 0 { dstEntry.entry.Name = UnresolvedNodeName } s.addEdge(srcEntry, dstEntry, p) } func (s *serviceMap) GetStatus() ServiceMapStatus { status := ServiceMapDisabled if s.IsEnabled() { status = ServiceMapEnabled } return ServiceMapStatus{ Status: status, EntriesProcessedCount: s.entriesProcessed, NodeCount: s.GetNodesCount(), EdgeCount: s.GetEdgesCount(), } } func (s *serviceMap) GetNodes() []ServiceMapNode { var nodes []ServiceMapNode for i, n := range s.graph.Nodes { nodes = append(nodes, ServiceMapNode{ Id: n.id, Name: string(i), Entry: n.entry, Count: n.count, }) } return nodes } func (s *serviceMap) GetEdges() []ServiceMapEdge { var edges []ServiceMapEdge for u, m := range s.graph.Edges { for v := range m { for _, p := range s.graph.Edges[u][v].data { edges = append(edges, ServiceMapEdge{ Source: ServiceMapNode{ Id: s.graph.Nodes[u].id, Name: string(u), Entry: s.graph.Nodes[u].entry, Count: s.graph.Nodes[u].count, }, Destination: ServiceMapNode{ Id: s.graph.Nodes[v].id, Name: string(v), Entry: s.graph.Nodes[v].entry, Count: s.graph.Nodes[v].count, }, Count: p.count, Protocol: p.protocol, }) } } } return edges } func (s *serviceMap) GetEntriesProcessedCount() int { return s.entriesProcessed } func (s *serviceMap) GetNodesCount() int { return len(s.graph.Nodes) } func (s *serviceMap) GetEdgesCount() int { var count int for u, m := range s.graph.Edges { for v := range m { for range s.graph.Edges[u][v].data { count++ } } } return count } func (s *serviceMap) Reset() { s.entriesProcessed = 0 s.graph = newDirectedGraph() }